From bf6cb685c587db43368de7c9890884edafaf7932 Mon Sep 17 00:00:00 2001 From: gayanMatch Date: Fri, 16 Jan 2026 19:35:39 -0600 Subject: [PATCH] fix: refactor chatStore from tasks dictionary to single task object (#627) --- src/components/AddWorker/index.tsx | 6 +- .../ChatBox/MessageItem/NoticeCard.tsx | 12 +- .../ChatBox/ProjectChatContainer.tsx | 22 +- src/components/ChatBox/ProjectSection.tsx | 6 +- src/components/ChatBox/TaskBox/TaskCard.tsx | 14 +- src/components/ChatBox/UserQueryGroup.tsx | 37 +- src/components/ChatBox/index.tsx | 255 ++- src/components/Folder/index.tsx | 20 +- .../GroupedHistoryView/ProjectGroup.tsx | 11 +- src/components/HistorySidebar/index.tsx | 24 +- src/components/Layout/index.tsx | 6 +- src/components/SearchAgentWrokSpace/index.tsx | 9 +- src/components/SearchHistoryDialog.tsx | 2 +- src/components/TaskState/index.tsx | 2 +- src/components/Terminal/index.tsx | 4 +- .../TerminalAgentWrokSpace/index.tsx | 9 +- src/components/TopBar/index.tsx | 38 +- src/components/WorkFlow/node.tsx | 28 +- src/components/WorkSpaceMenu/index.tsx | 77 +- src/lib/replay.ts | 6 +- src/pages/Dashboard/Project.tsx | 34 +- src/pages/Home.tsx | 52 +- src/pages/Setting/General.tsx | 2 +- src/store/chatStore.ts | 1548 ++++++++--------- src/store/projectStore.ts | 28 +- 25 files changed, 1059 insertions(+), 1193 deletions(-) diff --git a/src/components/AddWorker/index.tsx b/src/components/AddWorker/index.tsx index c06d8cd7..766286bc 100644 --- a/src/components/AddWorker/index.tsx +++ b/src/components/AddWorker/index.tsx @@ -60,8 +60,8 @@ export function AddWorker({ return
Loading...
; } const activeProjectId = projectStore.activeProjectId; - const activeTaskId = chatStore.activeTaskId; - const tasks = chatStore.tasks; + const activeTaskId = chatStore.taskId; + const task = chatStore.task; const [showEnvConfig, setShowEnvConfig] = useState(false); const [activeMcp, setActiveMcp] = useState(null); const [envValues, setEnvValues] = useState<{ [key: string]: EnvValue }>({}); @@ -301,7 +301,7 @@ export function AddWorker({ }); setWorkerList(newWorkerList); } else if ( - activeTaskId && tasks[activeTaskId].messages.length === 0 + activeTaskId && task && task.messages.length === 0 ) { const worker: Agent = { tasks: [], diff --git a/src/components/ChatBox/MessageItem/NoticeCard.tsx b/src/components/ChatBox/MessageItem/NoticeCard.tsx index ab606a85..ed3e5817 100644 --- a/src/components/ChatBox/MessageItem/NoticeCard.tsx +++ b/src/components/ChatBox/MessageItem/NoticeCard.tsx @@ -34,7 +34,7 @@ export function NoticeCard() { }, 100); } }, [ - chatStore.tasks[chatStore.activeTaskId as string].cotList.length, + chatStore.task?.cotList?.length, isExpanded, ]); @@ -62,16 +62,16 @@ export function NoticeCard() { isExpanded ? "overflow-y-auto" : "overflow-y-auto max-h-[200px]" } transition-all duration-300 ease-in-out scrollbar-hide relative`} style={{ - maskImage: isExpanded - ? 'none' + maskImage: isExpanded + ? 'none' : 'linear-gradient(to top, black 0%, black 40%, transparent 100%)', - WebkitMaskImage: isExpanded - ? 'none' + WebkitMaskImage: isExpanded + ? 'none' : 'linear-gradient(to top, black 0%, black 40%, transparent 100%)' }} >
- {chatStore.tasks[chatStore.activeTaskId as string].cotList.map( + {(chatStore.task?.cotList || []).map( (cot: string, index: number) => { return (
= ({ useEffect(() => { if (!chatStore || !activeProjectId) return; - const activeTaskId = chatStore.activeTaskId; + const activeTaskId = chatStore.taskId; if (!activeTaskId) return; - const task = chatStore.tasks[activeTaskId]; + const task = chatStore.task; if (!task) return; const currentMessageCount = task.messages.length; - + // Check if a new user message was added if (currentMessageCount > lastMessageCount) { const lastMessage = task.messages[task.messages.length - 1]; - + // If the last message is from user, scroll to bottom if (lastMessage && lastMessage.role === 'user') { scrollToBottom(); } } - + setLastMessageCount(currentMessageCount); - }, [chatStore?.tasks[chatStore.activeTaskId as string]?.messages, lastMessageCount, scrollToBottom, activeProjectId]); + }, [chatStore?.task?.messages, lastMessageCount, scrollToBottom, activeProjectId]); // Reset message count when active task changes useEffect(() => { setLastMessageCount(0); - }, [chatStore?.activeTaskId]); + }, [chatStore?.taskId]); // Intersection Observer for scroll-based animations useEffect(() => { @@ -142,13 +142,13 @@ export const ProjectChatContainer: React.FC = ({ {chatStores.map(({ chatId, chatStore }) => { const chatState = chatStore.getState(); - const activeTaskId = chatState.activeTaskId; - - if (!activeTaskId || !chatState.tasks[activeTaskId]) { + const activeTaskId = chatState.taskId; + + if (!activeTaskId || !chatState.task) { return null; } - const task = chatState.tasks[activeTaskId]; + const task = chatState.task; const messages = task.messages || []; // Only render if there are actual user messages (not just empty or system messages) diff --git a/src/components/ChatBox/ProjectSection.tsx b/src/components/ChatBox/ProjectSection.tsx index 8841766c..f7864f85 100644 --- a/src/components/ChatBox/ProjectSection.tsx +++ b/src/components/ChatBox/ProjectSection.tsx @@ -56,13 +56,13 @@ export const ProjectSection = React.forwardRef { return ( - chatStore.tasks[chatStore.activeTaskId as string].status === "finished" + chatStore.task?.status === "finished" ); - }, [chatStore.tasks[chatStore.activeTaskId as string].status]); + }, [chatStore.task?.status]); useEffect(() => { if ( - chatStore.tasks[chatStore.activeTaskId as string].activeWorkSpace === + chatStore.task?.activeWorkSpace === "workflow" ) { setIsExpanded(false); } else { setIsExpanded(true); } - }, [chatStore.tasks[chatStore.activeTaskId as string].activeWorkSpace]); + }, [chatStore.task?.activeWorkSpace]); // Improved height calculation logic useEffect(() => { @@ -347,11 +347,9 @@ export function TaskCard({ // Set the active workspace and agent chatStore.setActiveWorkSpace( - chatStore.activeTaskId as string, "workflow" ); chatStore.setActiveAgent( - chatStore.activeTaskId as string, task.agent?.agent_id ); window.electronAPI.hideAllWebview(); @@ -386,9 +384,7 @@ export function TaskCard({ )} diff --git a/src/components/ChatBox/UserQueryGroup.tsx b/src/components/ChatBox/UserQueryGroup.tsx index d0b5faa8..4eed81e5 100644 --- a/src/components/ChatBox/UserQueryGroup.tsx +++ b/src/components/ChatBox/UserQueryGroup.tsx @@ -37,16 +37,15 @@ export const UserQueryGroup: React.FC = ({ const taskBoxRef = useRef(null); const [isTaskBoxSticky, setIsTaskBoxSticky] = useState(false); const chatState = chatStore.getState(); - const activeTaskId = chatState.activeTaskId; + const activeTaskId = chatState.taskId; // Subscribe to streaming decompose text separately for efficient updates const streamingDecomposeText = useSyncExternalStore( (callback) => chatStore.subscribe(callback), () => { const state = chatStore.getState(); - const taskId = state.activeTaskId; - if (!taskId || !state.tasks[taskId]) return ''; - return state.tasks[taskId].streamingDecomposeText || ''; + if (!state.taskId || !state.task) return ''; + return state.task.streamingDecomposeText || ''; } ); @@ -55,11 +54,11 @@ export const UserQueryGroup: React.FC = ({ // Exclude human-reply scenarios (when user is replying to an activeAsk) const isHumanReply = queryGroup.userMessage && activeTaskId && - chatState.tasks[activeTaskId] && - (chatState.tasks[activeTaskId].activeAsk || + chatState.task && + (chatState.task.activeAsk || // Check if this user message follows an 'ask' message in the message sequence (() => { - const messages = chatState.tasks[activeTaskId].messages; + const messages = chatState.task.messages; const userMessageIndex = messages.findIndex((m: any) => m.id === queryGroup.userMessage.id); if (userMessageIndex > 0) { // Check the previous message - if it's an agent message with step 'ask', this is a human-reply @@ -72,22 +71,22 @@ export const UserQueryGroup: React.FC = ({ const isLastUserQuery = !queryGroup.taskMessage && !isHumanReply && activeTaskId && - chatState.tasks[activeTaskId] && + chatState.task && queryGroup.userMessage && - queryGroup.userMessage.id === chatState.tasks[activeTaskId].messages.filter((m: any) => m.role === 'user').pop()?.id && + queryGroup.userMessage.id === chatState.task.messages.filter((m: any) => m.role === 'user').pop()?.id && // Only show during active phases (not finished) - chatState.tasks[activeTaskId].status !== 'finished'; + chatState.task.status !== 'finished'; // Only show the fallback task box for the newest query while the agent is still splitting work. // Simple Q&A sessions set hasWaitComfirm to true, so we should not render an empty task box there. // Also, do not show fallback task if we are currently decomposing (streaming text). const isDecomposing = streamingDecomposeText.length > 0; const shouldShowFallbackTask = - isLastUserQuery && activeTaskId && !chatState.tasks[activeTaskId].hasWaitComfirm && !isDecomposing; + isLastUserQuery && activeTaskId && !chatState.task?.hasWaitComfirm && !isDecomposing; const task = (queryGroup.taskMessage || shouldShowFallbackTask) && activeTaskId - ? chatState.tasks[activeTaskId] + ? chatState.task : null; // Set up intersection observer for this query group @@ -230,15 +229,15 @@ export const UserQueryGroup: React.FC = ({ progressValue={task?.progressValue || 0} summaryTask={task?.summaryTask || ""} onAddTask={() => { - chatState.setIsTaskEdit(activeTaskId as string, true); + chatState.setIsTaskEdit(true); chatState.addTaskInfo(); }} onUpdateTask={(taskIndex, content) => { - chatState.setIsTaskEdit(activeTaskId as string, true); + chatState.setIsTaskEdit(true); chatState.updateTaskInfo(taskIndex, content); }} onDeleteTask={(taskIndex) => { - chatState.setIsTaskEdit(activeTaskId as string, true); + chatState.setIsTaskEdit(true); chatState.deleteTaskInfo(taskIndex); }} clickable={true} @@ -279,8 +278,8 @@ export const UserQueryGroup: React.FC = ({ animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.3 }} onClick={() => { - chatState.setSelectedFile(activeTaskId as string, file); - chatState.setActiveWorkSpace(activeTaskId as string, "documentWorkSpace"); + chatState.setSelectedFile(file); + chatState.setActiveWorkSpace("documentWorkSpace"); }} className="flex items-center gap-2 bg-message-fill-default rounded-sm px-2 py-1 w-[140px] cursor-pointer hover:bg-message-fill-hover transition-colors" > @@ -356,8 +355,8 @@ export const UserQueryGroup: React.FC = ({ animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.3 }} onClick={() => { - chatState.setSelectedFile(activeTaskId as string, file); - chatState.setActiveWorkSpace(activeTaskId as string, "documentWorkSpace"); + chatState.setSelectedFile(file); + chatState.setActiveWorkSpace("documentWorkSpace"); }} className="flex items-center gap-2 bg-message-fill-default rounded-2xl px-2 py-1 w-[120px] cursor-pointer hover:bg-message-fill-hover transition-colors" > diff --git a/src/components/ChatBox/index.tsx b/src/components/ChatBox/index.tsx index c4e098a2..b98c83d6 100644 --- a/src/components/ChatBox/index.tsx +++ b/src/components/ChatBox/index.tsx @@ -92,17 +92,17 @@ export default function ChatBox(): JSX.Element { const navigate = useNavigate(); const handleSend = async (messageStr?: string, taskId?: string) => { - const _taskId = taskId || chatStore.activeTaskId; + const _taskId = taskId || chatStore.taskId; if (message.trim() === "" && !messageStr) return; const tempMessageContent = messageStr || message; - chatStore.setHasMessages(_taskId as string, true); + chatStore.setHasMessages(true); if (!_taskId) return; // Multi-turn support: Check if task is running or planning (splitting/confirm) - const task = chatStore.tasks[_taskId]; + const task = chatStore.task; const requiresHumanReply = Boolean(task?.activeAsk); const isTaskInProgress = ["running", "pause"].includes(task?.status || ""); - const isTaskBusy = ( + const isTaskBusy = task ? ( // running or paused counts as busy (task.status === 'running' && task.hasMessages) || task.status === 'pause' || // splitting phase: has to_sub_tasks not confirmed OR skeleton computing @@ -110,7 +110,7 @@ export default function ChatBox(): JSX.Element { ((!task.messages.find(m => m.step === 'to_sub_tasks') && !task.hasWaitComfirm && task.messages.length > 0) || task.isTakeControl) || // explicit confirm wait while task is pending but card not confirmed yet (!!task.messages.find(m => m.step === 'to_sub_tasks' && !m.isConfirm) && task.status === 'pending') - ); + ) : false; const isReplayChatStore = task?.type === "replay"; if (!requiresHumanReply && isTaskBusy && !isReplayChatStore) { toast.error("Current task is in progress. Please wait for it to finish before sending a new request.", { @@ -122,48 +122,48 @@ export default function ChatBox(): JSX.Element { if (textareaRef.current) textareaRef.current.style.height = "60px"; try { if (requiresHumanReply) { - chatStore.addMessages(_taskId, { + chatStore.addMessages({ id: generateUniqueId(), role: "user", content: tempMessageContent, attaches: - JSON.parse(JSON.stringify(chatStore.tasks[_taskId]?.attaches)) || [], + JSON.parse(JSON.stringify(chatStore.task?.attaches)) || [], }); setMessage(""); - + // Scroll to bottom after adding user message setTimeout(() => { scrollToBottom(); }, 200); - chatStore.setIsPending(_taskId, true); + chatStore.setIsPending(true); await fetchPost(`/chat/${projectStore.activeProjectId}/human-reply`, { - agent: chatStore.tasks[_taskId].activeAsk, + agent: chatStore.task?.activeAsk, reply: tempMessageContent, }); - if (chatStore.tasks[_taskId].askList.length === 0) { - chatStore.setActiveAsk(_taskId, ""); + if ((chatStore.task?.askList?.length ?? 0) === 0) { + chatStore.setActiveAsk(""); } else { - let activeAskList = chatStore.tasks[_taskId].askList; + let activeAskList = [...(chatStore.task?.askList || [])]; console.log( "activeAskList", JSON.parse(JSON.stringify(activeAskList)) ); let message = activeAskList.shift(); - chatStore.setActiveAskList(_taskId, [...activeAskList]); - chatStore.setActiveAsk(_taskId, message?.agent_name || ""); - chatStore.setIsPending(_taskId, false); - chatStore.addMessages(_taskId, message!); + chatStore.setActiveAskList([...activeAskList]); + chatStore.setActiveAsk(message?.agent_name || ""); + chatStore.setIsPending(false); + chatStore.addMessages(message!); } } else { // Check if we should continue the conversation or start a new task - const hasMessages = chatStore.tasks[_taskId as string].messages.length > 0; - const isFinished = chatStore.tasks[_taskId as string].status === "finished"; - const hasWaitComfirm = chatStore.tasks[_taskId as string]?.hasWaitComfirm; + const hasMessages = (chatStore.task?.messages?.length ?? 0) > 0; + const isFinished = chatStore.task?.status === "finished"; + const hasWaitComfirm = chatStore.task?.hasWaitComfirm; // Check if this task was manually stopped (finished but without natural completion) - const wasTaskStopped = isFinished && !chatStore.tasks[_taskId as string].messages.some( + const wasTaskStopped = isFinished && !chatStore.task?.messages?.some( m => m.step === "end" // Natural completion has an "end" step message ); @@ -171,29 +171,29 @@ export default function ChatBox(): JSX.Element { // 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 // 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 && !wasTaskStopped) || (isFinished && !wasTaskStopped) || (hasMessages && chatStore.task?.status === "pending"); if (shouldContinueConversation) { // Check if this is the very first message and task hasn't started - const hasSimpleResponse = chatStore.tasks[_taskId as string].messages.some( + const hasSimpleResponse = chatStore.task?.messages?.some( m => m.step === "wait_confirm" ); - const hasComplexTask = chatStore.tasks[_taskId as string].messages.some( + const hasComplexTask = chatStore.task?.messages?.some( m => m.step === "to_sub_tasks" ); - const hasErrorMessage = chatStore.tasks[_taskId as string].messages.some( + const hasErrorMessage = chatStore.task?.messages?.some( m => m.role === "agent" && m.content.startsWith("❌ **Error**:") ); // Only start a new task if: pending, no messages processed yet // OR while or after replaying a project - if ((chatStore.tasks[_taskId as string].status === "pending" && !hasSimpleResponse && !hasComplexTask && !isFinished) - || chatStore.tasks[_taskId].type === "replay" || hasErrorMessage) { + if ((chatStore.task?.status === "pending" && !hasSimpleResponse && !hasComplexTask && !isFinished) + || chatStore.task?.type === "replay" || hasErrorMessage) { setMessage(""); // Pass the message content to startTask instead of adding it to current chatStore - const attachesToSend = JSON.parse(JSON.stringify(chatStore.tasks[_taskId]?.attaches)) || []; + const attachesToSend = JSON.parse(JSON.stringify(chatStore.task?.attaches)) || []; try { - await chatStore.startTask(_taskId, undefined, undefined, undefined, tempMessageContent, attachesToSend); + await chatStore.startTask(undefined, undefined, undefined, tempMessageContent, attachesToSend); } catch (err: any) { console.error("Failed to start task:", err); toast.error(err?.message || "Failed to start task. Please check your model configuration."); @@ -214,15 +214,15 @@ export default function ChatBox(): JSX.Element { question: tempMessageContent, task_id: nextTaskId }); - chatStore.setIsPending(_taskId, true); + chatStore.setIsPending(true); // Add the user message to show it in UI - chatStore.addMessages(_taskId, { + chatStore.addMessages({ id: generateUniqueId(), role: "user", content: tempMessageContent, - attaches: JSON.parse(JSON.stringify(chatStore.tasks[_taskId]?.attaches)) || [], + attaches: JSON.parse(JSON.stringify(chatStore.task?.attaches)) || [], }); - chatStore.setAttaches(_taskId, []); + chatStore.setAttaches([]); setMessage(""); } } else { @@ -246,13 +246,13 @@ export default function ChatBox(): JSX.Element { setTimeout(() => { scrollToBottom(); }, 200); - + // For the very first message, add it to the current chatStore first, then call startTask - const attachesToSend = JSON.parse(JSON.stringify(chatStore.tasks[_taskId]?.attaches)) || []; + const attachesToSend = JSON.parse(JSON.stringify(chatStore.task?.attaches)) || []; setMessage(""); try { - await chatStore.startTask(_taskId, undefined, undefined, undefined, tempMessageContent, attachesToSend); - chatStore.setHasWaitComfirm(_taskId as string, true); + await chatStore.startTask(undefined, undefined, undefined, tempMessageContent, attachesToSend); + chatStore.setHasWaitComfirm(true); } catch (err: any) { console.error("Failed to start task:", err); toast.error(err?.message || "Failed to start task. Please check your model configuration."); @@ -282,20 +282,19 @@ export default function ChatBox(): JSX.Element { return; } let _token: string = token.split("__")[0]; - let taskId: string = token.split("__")[1]; - chatStore.create(taskId, "share"); - chatStore.setHasMessages(taskId, true); + let shareTaskId: string = token.split("__")[1]; + chatStore.create(shareTaskId, "share"); + chatStore.setHasMessages(true); const res = await proxyFetchGet(`/api/chat/share/info/${_token}`); if (res?.question) { - chatStore.addMessages(taskId, { + chatStore.addMessages({ id: generateUniqueId(), role: "user", content: res.question.split("|")[0], }); try { - await chatStore.startTask(taskId, "share", _token, 0.1); - chatStore.setActiveTaskId(taskId); - chatStore.handleConfirmTask(projectStore.activeProjectId, taskId, "share"); + await chatStore.startTask("share", _token, 0.1); + chatStore.handleConfirmTask(projectStore.activeProjectId, "share"); } catch (err: any) { console.error("Failed to start shared task:", err); toast.error(err?.message || "Failed to start task. Please check your model configuration."); @@ -352,13 +351,12 @@ export default function ChatBox(): JSX.Element { }, []); const [loading, setLoading] = useState(false); - const handleConfirmTask = async (taskId?: string) => { - const _taskId = taskId || chatStore.activeTaskId; - if (!_taskId || !projectStore.activeProjectId) { + const handleConfirmTask = async () => { + if (!chatStore.taskId || !projectStore.activeProjectId) { return; } setLoading(true); - await chatStore.handleConfirmTask(projectStore.activeProjectId, _taskId); + await chatStore.handleConfirmTask(projectStore.activeProjectId); setLoading(false); }; @@ -371,14 +369,13 @@ export default function ChatBox(): JSX.Element { }); if (result.success && result.files && result.files.length > 0) { - const taskId = chatStore.activeTaskId as string; const files = [ - ...chatStore.tasks[taskId].attaches.filter( + ...(chatStore.task?.attaches || []).filter( (f) => !result.files.find((r: File) => r.filePath === f.filePath) ), ...result.files, ]; - chatStore.setAttaches(taskId, files); + chatStore.setAttaches(files); } } catch (error) { console.error("Select File Error:", error); @@ -396,23 +393,23 @@ export default function ChatBox(): JSX.Element { // Pause/Resume handler const [isPauseResumeLoading, setIsPauseResumeLoading] = useState(false); const handlePauseResume = () => { - const taskId = chatStore.activeTaskId as string; - const task = chatStore.tasks[taskId]; + const task = chatStore.task; + if (!task) return; const type = task.status === 'running' ? 'pause' : 'resume'; - + setIsPauseResumeLoading(true); if (type === 'pause') { let { taskTime, elapsed } = task; const now = Date.now(); elapsed += now - taskTime; - chatStore.setElapsed(taskId, elapsed); - chatStore.setTaskTime(taskId, 0); - chatStore.setStatus(taskId, 'pause'); + chatStore.setElapsed(elapsed); + chatStore.setTaskTime(0); + chatStore.setStatus('pause'); } else { - chatStore.setTaskTime(taskId, Date.now()); - chatStore.setStatus(taskId, 'running'); + chatStore.setTaskTime(Date.now()); + chatStore.setStatus('running'); } - + fetchPut(`/task/${projectStore.activeProjectId}/take-control`, { action: type, }); @@ -421,7 +418,7 @@ export default function ChatBox(): JSX.Element { // Stop task handler - triggers Action.skip_task which preserves context const handleSkip = async () => { - const taskId = chatStore.activeTaskId as string; + const taskId = chatStore.taskId as string; console.log("=" .repeat(80)); console.log("🛑 [STOP-BUTTON] handleSkip CALLED from frontend"); console.log(`[STOP-BUTTON] taskId: ${taskId}, projectId: ${projectStore.activeProjectId}`); @@ -443,7 +440,7 @@ export default function ChatBox(): JSX.Element { console.log("[STOP-BUTTON] ⚠️ SSE connection kept alive, waiting for backend 'end' event"); // Only set isPending to false so UI shows task is stopped - chatStore.setIsPending(taskId, false); + chatStore.setIsPending(false); console.log("[STOP-BUTTON] ✅ Task marked as not pending, SSE connection remains open"); toast.success("Task stopped successfully", { @@ -455,8 +452,8 @@ export default function ChatBox(): JSX.Element { // If backend call failed, close SSE connection as fallback console.log("[STOP-BUTTON] Backend call failed, closing SSE connection as fallback"); try { - chatStore.stopTask(taskId); - chatStore.setIsPending(taskId, false); + chatStore.stopTask(); + chatStore.setIsPending(false); console.log("[STOP-BUTTON] ⚠️ SSE connection closed due to backend failure"); toast.warning("Task stopped locally, but backend notification failed. Backend task may continue running.", { closeButton: true, @@ -476,20 +473,20 @@ export default function ChatBox(): JSX.Element { // Edit query handler const handleEditQuery = async () => { - const taskId = chatStore.activeTaskId as string; + const taskId = chatStore.taskId as string; const projectId = projectStore.activeProjectId; // Early validation - if (!projectId) { + if (!projectId || !chatStore.task) { console.error("No active project ID found for edit operation"); return; } // Get question and attachments before any deletions - const messageIndex = chatStore.tasks[taskId].messages.findLastIndex( + const messageIndex = chatStore.task.messages.findLastIndex( (item) => item.step === "to_sub_tasks" ); - const questionMessage = chatStore.tasks[taskId].messages[messageIndex - 2]; + const questionMessage = chatStore.task.messages[messageIndex - 2]; const question = questionMessage.content; // Get the file attachments from the original user message (not from task.attaches which gets cleared after sending) const attachments = questionMessage.attaches || []; @@ -516,56 +513,56 @@ export default function ChatBox(): JSX.Element { // Create new task and clean up locally let id = chatStore.create(); - chatStore.setHasMessages(id, true); + chatStore.setHasMessages(true); // Copy the file attachments to the new task if (attachments.length > 0) { - chatStore.setAttaches(id, attachments); + chatStore.setAttaches(attachments); } - chatStore.removeTask(taskId); + chatStore.removeTask(); setMessage(question); }; // Task time tracking const [taskTime, setTaskTime] = useState( - chatStore.getFormattedTaskTime(chatStore.activeTaskId as string) + chatStore.getFormattedTaskTime() ); useEffect(() => { const interval = setInterval(() => { - if (chatStore.activeTaskId) { + if (chatStore.taskId) { setTaskTime( - chatStore.getFormattedTaskTime(chatStore.activeTaskId) + chatStore.getFormattedTaskTime() ); } }, 500); return () => clearInterval(interval); - }, [chatStore.activeTaskId]); + }, [chatStore.taskId]); // Determine BottomBox state const getBottomBoxState = () => { - if (!chatStore.activeTaskId) return "input"; - const task = chatStore.tasks[chatStore.activeTaskId]; + if (!chatStore.taskId) return "input"; + const task = chatStore.task; // Queued messages no longer change BottomBox state; QueuedBox renders independently // Check for any to_sub_tasks message (confirmed or not) - const anyToSubTasksMessage = task.messages.find((m) => m.step === "to_sub_tasks"); - const toSubTasksMessage = task.messages.find((m) => (m.step === "to_sub_tasks" && !m.isConfirm)); - + const anyToSubTasksMessage = task?.messages.find((m: Message) => m.step === "to_sub_tasks"); + const toSubTasksMessage = task?.messages.find((m: Message) => (m.step === "to_sub_tasks" && !m.isConfirm)); + // Determine if we're in the "splitting in progress" phase (skeleton visible) // Only show splitting if there's NO to_sub_tasks message yet (not even confirmed) const isSkeletonPhase = ( - task.status !== 'finished' && - !anyToSubTasksMessage && - !task.hasWaitComfirm && - task.messages.length > 0) || - (task.isTakeControl && !anyToSubTasksMessage); + task?.status !== 'finished' && + !anyToSubTasksMessage && + !task?.hasWaitComfirm && + (task?.messages?.length ?? 0) > 0) || + (task?.isTakeControl && !anyToSubTasksMessage); if (isSkeletonPhase) { return "splitting"; } // After splitting completes and TaskCard is awaiting user confirmation, // the Task becomes 'pending' and we show the confirm state. - if (toSubTasksMessage && !toSubTasksMessage.isConfirm && task.status === 'pending') { + if (toSubTasksMessage && !toSubTasksMessage.isConfirm && task?.status === 'pending') { return "confirm"; } @@ -575,11 +572,11 @@ export default function ChatBox(): JSX.Element { } // Check task status - if (task.status === 'running' || task.status === 'pause') { + if (task?.status === 'running' || task?.status === 'pause') { return "running"; } - if (task.status === 'finished' && task.type !== '') { + if (task?.status === 'finished' && task?.type !== '') { return "finished"; } @@ -589,22 +586,18 @@ export default function ChatBox(): JSX.Element { const [hasSubTask, setHasSubTask] = useState(false); useEffect(() => { - const _hasSubTask = chatStore.tasks[ - chatStore.activeTaskId as string - ]?.messages?.find((message) => message.step === "to_sub_tasks") + const _hasSubTask = chatStore.task?.messages?.find((message: Message) => message.step === "to_sub_tasks") ? true : false; setHasSubTask(_hasSubTask); - }, [chatStore?.tasks[chatStore.activeTaskId as string]?.messages]); + }, [chatStore.task?.messages]); useEffect(() => { - const activeAsk = - chatStore?.tasks[chatStore.activeTaskId as string]?.activeAsk; + const activeAsk = chatStore.task?.activeAsk; let timer: NodeJS.Timeout; if (activeAsk && activeAsk !== "") { - const _taskId = chatStore.activeTaskId as string; timer = setTimeout(() => { - handleSend("skip", _taskId); + handleSend("skip"); }, 30000); // 30 seconds return () => clearTimeout(timer); // clear previous timer } @@ -613,7 +606,7 @@ export default function ChatBox(): JSX.Element { clearTimeout(timer); }; }, [ - chatStore?.tasks[chatStore.activeTaskId as string]?.activeAsk, + chatStore.task?.activeAsk, message, // depend on message ]); @@ -648,7 +641,7 @@ export default function ChatBox(): JSX.Element { // Always try to call the backend to remove the task // The backend will handle the error gracefully if workforce is not initialized // Note: Replay creates a new chatstore, so no conflicts - const task = chatStore.tasks[chatStore.activeTaskId as string]; + const task = chatStore.task; // Only skip backend call if task is finished or hasn't started yet (no messages) if(task && task.messages.length > 0 && task.status !== 'finished') { try { @@ -677,42 +670,42 @@ export default function ChatBox(): JSX.Element { // Check if any chat store in the project has messages const hasAnyMessages = useMemo(() => { // First check current active chat store - if (chatStore.activeTaskId && chatStore.tasks[chatStore.activeTaskId]) { - const activeTask = chatStore.tasks[chatStore.activeTaskId]; + if (chatStore.taskId && chatStore.task) { + const activeTask = chatStore.task; if ((activeTask.messages && activeTask.messages.length > 0) || activeTask.hasMessages) { return true; } } // Then check all other chat stores in the project - return getAllChatStoresMemoized.some(({chatStore: store}) => { + return getAllChatStoresMemoized.some(({chatStore: store}: {chatStore: any}) => { const state = store.getState(); - return state.activeTaskId && - state.tasks[state.activeTaskId] && - (state.tasks[state.activeTaskId].messages.length > 0 || - state.tasks[state.activeTaskId].hasMessages); + return state.taskId && + state.task && + (state.task.messages.length > 0 || + state.task.hasMessages); }); }, [chatStore, getAllChatStoresMemoized]); const isTaskBusy = useMemo(() => { - if (!chatStore.activeTaskId || !chatStore.tasks[chatStore.activeTaskId]) return false; - const task = chatStore.tasks[chatStore.activeTaskId]; + if (!chatStore.taskId || !chatStore.task) return false; + const task = chatStore.task; return ( // running or paused - task.status === 'running' || + task.status === 'running' || task.status === 'pause' || // splitting phase - task.messages.some(m => m.step === 'to_sub_tasks' && !m.isConfirm) || + task.messages.some((m: Message) => m.step === 'to_sub_tasks' && !m.isConfirm) || // skeleton/computing phase - ((!task.messages.find(m => m.step === 'to_sub_tasks') && !task.hasWaitComfirm && task.messages.length > 0) || task.isTakeControl) + ((!task.messages.find((m: Message) => m.step === 'to_sub_tasks') && !task.hasWaitComfirm && task.messages.length > 0) || task.isTakeControl) ); - }, [chatStore.activeTaskId, chatStore.tasks]); + }, [chatStore.taskId, chatStore.task]); const isInputDisabled = useMemo(() => { - if (!chatStore.activeTaskId || !chatStore.tasks[chatStore.activeTaskId]) return true; - - const task = chatStore.tasks[chatStore.activeTaskId]; - + if (!chatStore.taskId || !chatStore.task) return true; + + const task = chatStore.task; + // If ask human is active, allow input if (task.activeAsk) return false; @@ -725,8 +718,8 @@ export default function ChatBox(): JSX.Element { return false; }, [ - chatStore.activeTaskId, - chatStore.tasks, + chatStore.taskId, + chatStore.task, privacy, useCloudModelInDev, isTaskBusy @@ -742,7 +735,7 @@ export default function ChatBox(): JSX.Element { onSkip={handleSkip} isPauseResumeLoading={isPauseResumeLoading} /> - {chatStore.activeTaskId && ( + {chatStore.taskId && ( ({ @@ -751,21 +744,21 @@ export default function ChatBox(): JSX.Element { timestamp: m.timestamp })) || []} onRemoveQueuedMessage={(id) => handleRemoveTaskQueue(id)} - subtitle={getBottomBoxState() === 'confirm' + subtitle={getBottomBoxState() === 'confirm' ? (() => { // Find the last message where role is "user" - const messages = chatStore.tasks[chatStore.activeTaskId]?.messages || []; - const lastUserMessage = messages.slice().reverse().find(msg => msg.role === "user"); - return lastUserMessage?.content || chatStore.tasks[chatStore.activeTaskId]?.summaryTask; + const messages = chatStore.task?.messages || []; + const lastUserMessage = messages.slice().reverse().find((msg: Message) => msg.role === "user"); + return lastUserMessage?.content || chatStore.task?.summaryTask; })() - : chatStore.tasks[chatStore.activeTaskId]?.summaryTask} + : chatStore.task?.summaryTask} onStartTask={() => handleConfirmTask()} onEdit={handleEditQuery} - tokens={chatStore.tasks[chatStore.activeTaskId]?.tokens || 0} + tokens={chatStore.task?.tokens || 0} taskTime={taskTime} - taskStatus={chatStore.tasks[chatStore.activeTaskId]?.status} + taskStatus={chatStore.task?.status} onReplay={handleReplay} - replayDisabled={chatStore.tasks[chatStore.activeTaskId]?.status !== 'finished'} + replayDisabled={chatStore.task?.status !== 'finished'} replayLoading={isReplayLoading} onPauseResume={handlePauseResume} pauseResumeLoading={isPauseResumeLoading} @@ -774,11 +767,11 @@ export default function ChatBox(): JSX.Element { value: message, onChange: setMessage, onSend: handleSend, - files: chatStore.tasks[chatStore.activeTaskId]?.attaches?.map(f => ({ + files: chatStore.task?.attaches?.map((f: any) => ({ fileName: f.fileName, filePath: f.filePath })) || [], - onFilesChange: (files) => chatStore.setAttaches(chatStore.activeTaskId as string, files as any), + onFilesChange: (files) => chatStore.setAttaches(files as any), onAddFile: handleFileSelect, placeholder: t("chat.ask-placeholder"), disabled: isInputDisabled, @@ -804,18 +797,18 @@ export default function ChatBox(): JSX.Element {
- {chatStore.activeTaskId && ( + {chatStore.taskId && ( ({ + files: chatStore.task?.attaches?.map((f: any) => ({ fileName: f.fileName, filePath: f.filePath })) || [], - onFilesChange: (files) => chatStore.setAttaches(chatStore.activeTaskId as string, files as any), + onFilesChange: (files) => chatStore.setAttaches(files as any), onAddFile: handleFileSelect, placeholder: t("chat.ask-placeholder"), disabled: isInputDisabled, diff --git a/src/components/Folder/index.tsx b/src/components/Folder/index.tsx index b1281622..cabb6202 100644 --- a/src/components/Folder/index.tsx +++ b/src/components/Folder/index.tsx @@ -189,7 +189,7 @@ export default function Folder({ data }: { data?: Agent }) { .invoke("read-file-dataurl", file.path) .then((dataUrl: string) => { setSelectedFile({ ...file, content: dataUrl }); - chatStore.setSelectedFile(chatStore.activeTaskId as string, file); + chatStore.setSelectedFile(file); setLoading(false); }) .catch((error) => { @@ -204,7 +204,7 @@ export default function Folder({ data }: { data?: Agent }) { .invoke("open-file", file.type, file.path, isShowSourceCode) .then((res) => { setSelectedFile({ ...file, content: res }); - chatStore.setSelectedFile(chatStore.activeTaskId as string, file); + chatStore.setSelectedFile(file); setLoading(false); }) .catch((error) => { @@ -304,10 +304,10 @@ export default function Folder({ data }: { data?: Agent }) { const hasFetchedRemote = useRef(false); - // Reset hasFetchedRemote when activeTaskId changes + // Reset hasFetchedRemote when taskId changes useEffect(() => { hasFetchedRemote.current = false; - }, [chatStore.activeTaskId]); + }, [chatStore.taskId]); useEffect(() => { const setFileList = async () => { @@ -348,7 +348,7 @@ export default function Folder({ data }: { data?: Agent }) { // Keep the old structure for compatibility setFileGroups((prev) => { const chatStoreSelectedFile = - chatStore.tasks[chatStore.activeTaskId as string]?.selectedFile; + chatStore.task?.selectedFile; if (chatStoreSelectedFile) { console.log(res, chatStoreSelectedFile); const file = res.find( @@ -368,11 +368,11 @@ export default function Folder({ data }: { data?: Agent }) { }); }; setFileList(); - }, [chatStore.tasks[chatStore.activeTaskId as string]?.taskAssigning]); + }, [chatStore.task?.taskAssigning]); useEffect(() => { const chatStoreSelectedFile = - chatStore.tasks[chatStore.activeTaskId as string]?.selectedFile; + chatStore.task?.selectedFile; if (chatStoreSelectedFile && fileGroups[0]?.files) { const file = fileGroups[0].files.find( (item: any) => item.path === chatStoreSelectedFile.path @@ -382,14 +382,14 @@ export default function Folder({ data }: { data?: Agent }) { } } }, [ - chatStore.tasks[chatStore.activeTaskId as string]?.selectedFile?.path, + chatStore.task?.selectedFile?.path, fileGroups, isShowSourceCode, - chatStore.activeTaskId, + chatStore.taskId, ]); const handleBack = () => { - chatStore.setActiveWorkSpace(chatStore.activeTaskId as string, "workflow"); + chatStore.setActiveWorkSpace("workflow"); }; return ( diff --git a/src/components/GroupedHistoryView/ProjectGroup.tsx b/src/components/GroupedHistoryView/ProjectGroup.tsx index 041e2d33..fedcdd28 100644 --- a/src/components/GroupedHistoryView/ProjectGroup.tsx +++ b/src/components/GroupedHistoryView/ProjectGroup.tsx @@ -115,18 +115,17 @@ export default function ProjectGroup({ // Calculate if project has issues (requiring human in the loop) // Find tasks in chatStore where task_id matches any task in the project const hasHumanInLoop = useMemo(() => { - if (!chatStore?.tasks || !project.tasks?.length) return false; + if (!chatStore?.task || !project.tasks?.length) return false; // Get all task_ids from the project, filtering out undefined/null values const projectTaskIds = project.tasks .map(task => task.task_id) .filter((id): id is string => !!id); - // Check if any task in chatStore with matching task_id has pending status - return Object.entries(chatStore.tasks).some(([taskId, task]) => - projectTaskIds.includes(taskId) && task.status === 'pending' - ); - }, [chatStore?.tasks, project.tasks]); + // Check if current task matches any project task and has pending status + const currentTaskId = chatStore.taskId; + return currentTaskId && projectTaskIds.includes(currentTaskId) && chatStore.task.status === 'pending'; + }, [chatStore?.task, chatStore?.taskId, project.tasks]); const hasIssue = hasHumanInLoop; // Calculate agent count (placeholder - count unique agents from tasks if available) diff --git a/src/components/HistorySidebar/index.tsx b/src/components/HistorySidebar/index.tsx index c4fd5d85..73127523 100644 --- a/src/components/HistorySidebar/index.tsx +++ b/src/components/HistorySidebar/index.tsx @@ -101,20 +101,18 @@ export default function HistorySidebar() { // Check all chat stores for ongoing tasks chatStores.forEach(({ chatStore: cs }) => { const csState = cs.getState(); - Object.keys(csState.tasks || {}).forEach((taskId) => { - const task = csState.tasks[taskId]; - // Only include ongoing tasks - if (task.status !== "finished" && !task.type) { - hasOngoingTasks = true; - taskCount++; - if (task.tokens) { - totalTokens += task.tokens; - } - if (!lastPrompt && task.messages?.[0]?.content) { - lastPrompt = task.messages[0].content; - } + const task = csState.task; + // Only include ongoing tasks + if (task && task.status !== "finished" && !task.type) { + hasOngoingTasks = true; + taskCount++; + if (task.tokens) { + totalTokens += task.tokens; } - }); + if (!lastPrompt && task.messages?.[0]?.content) { + lastPrompt = task.messages[0].content; + } + } }); // Only add project if it has ongoing tasks diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx index 8e0a7814..aacfd303 100644 --- a/src/components/Layout/index.tsx +++ b/src/components/Layout/index.tsx @@ -40,8 +40,8 @@ const Layout = () => { useEffect(() => { const handleBeforeClose = () => { - const currentStatus = chatStore.tasks[chatStore.activeTaskId as string]?.status; - if(["running", "pause"].includes(currentStatus)) { + const currentStatus = chatStore.task?.status; + if(currentStatus && ["running", "pause"].includes(currentStatus)) { setNoticeOpen(true); } else { window.electronAPI.closeWindow(true); @@ -53,7 +53,7 @@ const Layout = () => { return () => { window.ipcRenderer.removeAllListeners("before-close"); }; - }, [chatStore.tasks, chatStore.activeTaskId]); + }, [chatStore.task, chatStore.taskId]); // Determine what to show based on states const shouldShowOnboarding = initState === "done" && isFirstLaunch && !isInstalling; diff --git a/src/components/SearchAgentWrokSpace/index.tsx b/src/components/SearchAgentWrokSpace/index.tsx index fb198a90..3ccf117c 100644 --- a/src/components/SearchAgentWrokSpace/index.tsx +++ b/src/components/SearchAgentWrokSpace/index.tsx @@ -78,12 +78,12 @@ export default function Home() { const [activeAgent, setActiveAgent] = useState(null); useEffect(() => { const taskAssigning = - chatStore.tasks[chatStore.activeTaskId as string]?.taskAssigning; + chatStore.task?.taskAssigning; if (taskAssigning) { const activeAgent = taskAssigning.find( (item) => item.agent_id === - chatStore.tasks[chatStore.activeTaskId as string]?.activeWorkSpace + chatStore.task?.activeWorkSpace ); setActiveAgent(() => { if (activeAgent) { @@ -93,8 +93,8 @@ export default function Home() { }); } }, [ - chatStore.tasks[chatStore.activeTaskId as string].taskAssigning, - chatStore.tasks[chatStore.activeTaskId as string].activeWorkSpace, + chatStore.task?.taskAssigning, + chatStore.task?.activeWorkSpace, ]); const [isTakeControl, setIsTakeControl] = useState(false); @@ -193,7 +193,6 @@ export default function Home() { variant="ghost" onClick={() => { chatStore.setActiveWorkSpace( - chatStore.activeTaskId as string, "workflow" ); }} diff --git a/src/components/SearchHistoryDialog.tsx b/src/components/SearchHistoryDialog.tsx index 3a74ad56..7075538f 100644 --- a/src/components/SearchHistoryDialog.tsx +++ b/src/components/SearchHistoryDialog.tsx @@ -104,7 +104,7 @@ export function SearchHistoryDialog() { onTaskSelect={handleSetActive} onTaskDelete={handleDelete} onTaskShare={handleShare} - activeTaskId={chatStore.activeTaskId || undefined} + activeTaskId={chatStore.taskId || undefined} /> ) : ( diff --git a/src/components/TaskState/index.tsx b/src/components/TaskState/index.tsx index f52d8526..b4841eb5 100644 --- a/src/components/TaskState/index.tsx +++ b/src/components/TaskState/index.tsx @@ -138,7 +138,7 @@ export const TaskState = ({ (isSelected("ongoing") || forceVisible) && "!text-icon-information" } ${ - chatStore.tasks[chatStore.activeTaskId as string]?.status === + chatStore.task?.status === "running" && "animate-spin" }`} /> diff --git a/src/components/Terminal/index.tsx b/src/components/Terminal/index.tsx index 1cb57e09..6ebafd41 100644 --- a/src/components/Terminal/index.tsx +++ b/src/components/Terminal/index.tsx @@ -305,7 +305,7 @@ export default function TerminalComponent({ } // if there is history data, re-write - if (chatStore.activeTaskId) { + if (chatStore.taskId) { const terminalData = content || []; if (terminalData.length > 0) { xtermRef.current.writeln("\x1b[90m--- Previous Output ---\x1b[0m"); @@ -332,7 +332,7 @@ export default function TerminalComponent({ // show prompt xtermRef.current.write(promptText); }, 200); - }, [chatStore.activeTaskId, showWelcome, instanceId]); + }, [chatStore.taskId, showWelcome, instanceId]); // render terminal component return ( diff --git a/src/components/TerminalAgentWrokSpace/index.tsx b/src/components/TerminalAgentWrokSpace/index.tsx index dc6ea08e..cf4c7638 100644 --- a/src/components/TerminalAgentWrokSpace/index.tsx +++ b/src/components/TerminalAgentWrokSpace/index.tsx @@ -80,12 +80,12 @@ export default function TerminalAgentWrokSpace() { const [activeAgent, setActiveAgent] = useState(null); useEffect(() => { const taskAssigning = - chatStore.tasks[chatStore.activeTaskId as string]?.taskAssigning; + chatStore.task?.taskAssigning; if (taskAssigning) { const activeAgent = taskAssigning.find( (item) => item.agent_id === - chatStore.tasks[chatStore.activeTaskId as string]?.activeWorkSpace + chatStore.task?.activeWorkSpace ); setActiveAgent(() => { if (activeAgent) { @@ -95,8 +95,8 @@ export default function TerminalAgentWrokSpace() { }); } }, [ - chatStore.tasks[chatStore.activeTaskId as string].taskAssigning, - chatStore.tasks[chatStore.activeTaskId as string].activeWorkSpace, + chatStore.task?.taskAssigning, + chatStore.task?.activeWorkSpace, ]); const [isTakeControl, setIsTakeControl] = useState(false); @@ -145,7 +145,6 @@ export default function TerminalAgentWrokSpace() { variant="ghost" onClick={() => { chatStore.setActiveWorkSpace( - chatStore.activeTaskId as string, "workflow" ); }} diff --git a/src/components/TopBar/index.tsx b/src/components/TopBar/index.tsx index 71ada441..c711b20b 100644 --- a/src/components/TopBar/index.tsx +++ b/src/components/TopBar/index.tsx @@ -116,17 +116,15 @@ function HeaderWin() { const activeTaskTitle = useMemo(() => { if ( - chatStore.activeTaskId && - chatStore.tasks[chatStore.activeTaskId as string]?.summaryTask + chatStore.taskId && + chatStore.task?.summaryTask ) { - return chatStore.tasks[ - chatStore.activeTaskId as string - ].summaryTask.split("|")[0]; + return chatStore.task.summaryTask.split("|")[0]; } return t("layout.new-project"); }, [ - chatStore.activeTaskId, - chatStore.tasks[chatStore.activeTaskId as string]?.summaryTask, + chatStore.taskId, + chatStore.task?.summaryTask, ]); const getReferFriendsLink = async () => { @@ -147,7 +145,7 @@ function HeaderWin() { //TODO: Mark ChatStore details as completed const handleEndProject = async () => { - const taskId = chatStore.activeTaskId; + const taskId = chatStore.taskId; const projectId = projectStore.activeProjectId; if (!taskId) { @@ -158,7 +156,7 @@ function HeaderWin() { const historyId = projectId ? projectStore.getHistoryId(projectId) : null; try { - const task = chatStore.tasks[taskId]; + const task = chatStore.task; // Stop the task if it's running if (task && task.status === 'running') { @@ -175,11 +173,11 @@ function HeaderWin() { } // Delete from history using historyId - if (historyId && task.status !== "finished") { + if (historyId && task?.status !== "finished") { try { await proxyFetchDelete(`/api/chat/history/${historyId}`); // Remove from local store - chatStore.removeTask(taskId); + chatStore.removeTask(); } catch (error) { console.log("History may not exist:", error); } @@ -312,12 +310,12 @@ function HeaderWin() { platform === "darwin" && "pr-2" } flex h-full items-center z-50 relative no-drag gap-1`} > - {chatStore.activeTaskId && - chatStore.tasks[chatStore.activeTaskId as string] && + {chatStore.taskId && + chatStore.task && ( - (chatStore.tasks[chatStore.activeTaskId as string]?.messages?.length || 0) > 0 || - chatStore.tasks[chatStore.activeTaskId as string]?.hasMessages || - chatStore.tasks[chatStore.activeTaskId as string]?.status !== 'pending' + (chatStore.task?.messages?.length || 0) > 0 || + chatStore.task?.hasMessages || + chatStore.task?.status !== 'pending' ) && ( - {chatStore.activeTaskId && chatStore.tasks[chatStore.activeTaskId as string] && ( + {chatStore.taskId && chatStore.task && ( {t("layout.report-bug")} diff --git a/src/components/WorkFlow/node.tsx b/src/components/WorkFlow/node.tsx index 3af1e022..3c8f7390 100644 --- a/src/components/WorkFlow/node.tsx +++ b/src/components/WorkFlow/node.tsx @@ -150,7 +150,7 @@ export function Node({ id, data }: NodeProps) { }; useEffect(() => { - if (chatStore.tasks[chatStore.activeTaskId as string]?.activeAgent === id) { + if (chatStore.task?.activeAgent === id) { const node = getNode(id); if (node) { setTimeout(() => { @@ -164,7 +164,7 @@ export function Node({ id, data }: NodeProps) { } } }, [ - chatStore.tasks[chatStore.activeTaskId as string]?.activeAgent, + chatStore.task?.activeAgent, id, setCenter, getNode, @@ -328,7 +328,7 @@ export function Node({ id, data }: NodeProps) { } ${ data.isEditMode ? "h-full" : "max-h-[calc(100vh-200px)]" } border-worker-border-default flex border border-solid rounded-xl overflow-hidden bg-worker-surface-primary ${ - chatStore.tasks[chatStore.activeTaskId as string].activeAgent === id + chatStore.task?.activeAgent === id ? `${agentMap[data.type]?.borderColor} z-50` : "border-worker-border-default z-10" } transition-all duration-300 ease-in-out ${ @@ -355,8 +355,7 @@ export function Node({ id, data }: NodeProps) { {isExpanded ? : } {!Object.keys(agentMap).find((key) => key === data.type) && - chatStore.tasks[chatStore.activeTaskId as string].messages - .length === 0 && ( + (chatStore.task?.messages?.length ?? 0) === 0 && (