Skip to content

fix(frontend): fix history chat cannot continue issue#1022

Open
parabala wants to merge 1 commit intowecode-ai:mainfrom
parabala:wegent/fix-history-chat-continue
Open

fix(frontend): fix history chat cannot continue issue#1022
parabala wants to merge 1 commit intowecode-ai:mainfrom
parabala:wegent/fix-history-chat-continue

Conversation

@parabala
Copy link
Copy Markdown
Collaborator

@parabala parabala commented Apr 23, 2026

Summary

修复历史聊天无法继续聊天的问题。

问题原因

当用户点击历史任务继续聊天时,如果 WebSocket 未连接,任务房间不会被加入。当 WebSocket 后续连接时,没有逻辑重新加入任务房间,导致聊天功能无法正常工作。

具体流程:

  1. 用户点击历史任务,setSelectedTask 被调用
  2. 如果此时 WebSocket 未连接 (isConnected = false),joinTask 直接返回错误
  3. 当 WebSocket 后续连接成功时,没有触发重新加入任务房间的逻辑
  4. 结果:用户发送消息无法通过 WebSocket 同步,聊天功能失效

修复方案

添加独立的 useEffect 监控 WebSocket 连接状态 (isConnected),当连接恢复且存在选中的任务时,自动重新加入任务房间。

joinTask 函数内部已有 deduplication 逻辑 (joinedTasksRef),所以多次调用是安全的。

变更内容

  • frontend/src/features/tasks/contexts/taskContext.tsx:
    • 添加 WebSocket 重连处理 useEffect
    • 更新注释说明 joinTask 的 deduplication 机制

测试计划

  • 在 WebSocket 断开时点击历史任务,等待 WebSocket 重连后验证可以正常发送消息
  • 验证正常场景下(WebSocket 已连接)点击历史任务不受影响
  • 验证切换任务时 leaveTask/joinTask 逻辑正常

Summary by CodeRabbit

  • Bug Fixes

    • Resubscribe selected tasks automatically after reconnects and avoid duplicate live subscriptions when switching tasks, preserving stable real-time updates.
    • Ensure team selection syncs correctly when task details load late by falling back to the task’s team info.
  • New Features

    • Prevent sending messages without a selected agent/team and show a localized error prompting users to choose an agent.
  • Documentation

    • Added English and Chinese localization strings for the new selection error and guidance.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

Refactors WebSocket room join/rejoin logic and chat-team validation: taskContext splits join logic into selection-driven and reconnection-driven effects; ChatArea falls back to selectedTask.team_id when detail lacks team; message send handler blocks sends when no team selected; new i18n strings added.

Changes

Cohort / File(s) Summary
Task Context: room join/reconnect
frontend/src/features/tasks/contexts/taskContext.tsx
Split WebSocket room management into two useEffects: one responds to selectedTask changes (leave previous, update ref, join new task when isConnected is true) and a separate effect re-joins on isConnected transitions. Removed isConnected from the selection effect; relies on joinTask deduplication.
Chat UI: team sync fallback
frontend/src/features/tasks/components/chat/ChatArea.tsx
ChatAreaContent now also reads selectedTask; team-selection effect falls back to selectedTask.team_id when selectedTaskDetail.team is absent and includes selectedTask in dependencies so fallback runs when history sets selectedTask.
Chat handlers: send validation & toast
frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
handleSendMessage now checks selectedTeam?.id before proceeding; if missing, shows a destructive toast (i18n keys with Chinese fallbacks) and returns early, preventing further send logic and state updates.
i18n: new error messages
frontend/src/i18n/locales/en/chat.json, frontend/src/i18n/locales/zh-CN/chat.json
Adds errors.team_not_selected and errors.team_not_selected_description localization keys in English and Chinese. Minor JSON formatting adjustment in zh-CN file.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰✨ I nudged the socket, hopped out, then in,
Rooms leave and rejoin — a tidy little spin.
If no team’s chosen, I pause and I peep,
A toast wakes the user, then back to my sleep. 🥕📡

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary issue being fixed: history chat cannot continue. It accurately reflects the main changes across the PR, which resolve the WebSocket reconnection problem preventing message sending after selecting historical tasks.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
frontend/src/features/tasks/contexts/taskContext.tsx (1)

718-760: Keep only one reconnect join path and fix the stale note.

Line 733 and Line 751 can both call joinTask(selectedTask.id) on reconnect because the selected-task effect already depends on isConnected. Deduplication makes this mostly safe, but Lines 756-760 now directly contradict the new separate reconnect effect.

Consider either:

  • keep the separate reconnect effect and remove isConnected from the selected-task effect dependencies, or
  • rely on the selected-task effect and remove the separate reconnect effect.
♻️ Proposed cleanup if keeping the separate reconnect effect
-  }, [selectedTask, leaveTask, joinTask, isConnected])
+  }, [selectedTask, leaveTask, joinTask])

   // Separate effect to handle WebSocket reconnection
   // When WebSocket reconnects, rejoin the current task room if there is one
   useEffect(() => {
     // Only proceed if WebSocket is connected and there is a selected task
     if (isConnected && selectedTask) {
       // Rejoin the task room to ensure we receive real-time updates
       // joinTask has built-in deduplication, so it's safe to call again
       joinTask(selectedTask.id)
     }
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [isConnected])
-
-  // NOTE: Removed separate isConnected useEffect to prevent duplicate joinTask calls.
-  // The selectedTask useEffect above now handles both cases:
-  // 1. When selectedTask changes (and isConnected is true)
-  // 2. When isConnected changes (and selectedTask is set)
-  // This is because we added isConnected to the dependency array.
+  }, [isConnected, selectedTask, joinTask])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/contexts/taskContext.tsx` around lines 718 - 760,
The code currently has two paths that can call joinTask(selectedTask.id) on
reconnect: the selected-task useEffect (which includes isConnected in its
dependency array) and the separate reconnection useEffect, causing
duplicate/contradictory logic; pick one approach and consolidate: either (A)
keep the separate reconnection effect and remove isConnected from the dependency
array of the selected-task useEffect (so selected-task effect only reacts to
selectedTask changes and uses previousTaskIdRef, leaveTask, joinTask,
refreshSelectedTaskDetail, setSelectedTaskDetail), or (B) remove the separate
reconnection useEffect entirely and keep isConnected in the selected-task
useEffect dependencies (so selected-task effect handles both selectedTask and
reconnect scenarios); update or remove the explanatory NOTE accordingly and
ensure only one place calls joinTask on reconnect to avoid duplicated logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/src/features/tasks/contexts/taskContext.tsx`:
- Around line 718-760: The code currently has two paths that can call
joinTask(selectedTask.id) on reconnect: the selected-task useEffect (which
includes isConnected in its dependency array) and the separate reconnection
useEffect, causing duplicate/contradictory logic; pick one approach and
consolidate: either (A) keep the separate reconnection effect and remove
isConnected from the dependency array of the selected-task useEffect (so
selected-task effect only reacts to selectedTask changes and uses
previousTaskIdRef, leaveTask, joinTask, refreshSelectedTaskDetail,
setSelectedTaskDetail), or (B) remove the separate reconnection useEffect
entirely and keep isConnected in the selected-task useEffect dependencies (so
selected-task effect handles both selectedTask and reconnect scenarios); update
or remove the explanatory NOTE accordingly and ensure only one place calls
joinTask on reconnect to avoid duplicated logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 426c0ac9-1402-4bf4-9853-c224b995a9c5

📥 Commits

Reviewing files that changed from the base of the PR and between b1b1d20 and a465998.

📒 Files selected for processing (1)
  • frontend/src/features/tasks/contexts/taskContext.tsx

@parabala parabala force-pushed the wegent/fix-history-chat-continue branch from a465998 to 87e86ee Compare April 23, 2026 11:13
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
frontend/src/features/tasks/contexts/taskContext.tsx (1)

744-755: Avoid suppressing hook dependencies here.

This effect reads selectedTask and joinTask but only depends on isConnected. A rising-edge guard keeps the “only on reconnect” behavior while satisfying hook dependency rules.

♻️ Proposed refactor
+  const wasConnectedRef = useRef(isConnected)
+
   // Effect 2: Handle WebSocket reconnection
   // When WebSocket reconnects (isConnected changes from false to true),
   // rejoin the current task room to resume real-time updates
   useEffect(() => {
+    const wasConnected = wasConnectedRef.current
+    wasConnectedRef.current = isConnected
+
     // Only proceed if WebSocket is connected and there is a selected task
-    if (isConnected && selectedTask) {
+    if (!wasConnected && isConnected && selectedTask) {
       // Rejoin the task room to ensure we receive real-time updates
       // joinTask has built-in deduplication (joinedTasksRef), so it's safe to call again
       joinTask(selectedTask.id)
     }
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [isConnected])
+  }, [isConnected, selectedTask, joinTask])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/contexts/taskContext.tsx` around lines 744 - 755,
The effect currently uses selectedTask and joinTask but only lists isConnected
in its dependency array and disables the lint rule; fix this by adding
selectedTask and joinTask to the dependencies and implement a rising-edge guard
(track previous isConnected with a ref like prevIsConnectedRef) so the body runs
only when prevIsConnectedRef.current is false and isConnected is true; ensure
you update prevIsConnectedRef after each render and call
joinTask(selectedTask.id) only when selectedTask exists (the effect still
preserves the “only on reconnect” behavior and avoids lint suppression).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/src/features/tasks/contexts/taskContext.tsx`:
- Around line 744-755: The effect currently uses selectedTask and joinTask but
only lists isConnected in its dependency array and disables the lint rule; fix
this by adding selectedTask and joinTask to the dependencies and implement a
rising-edge guard (track previous isConnected with a ref like
prevIsConnectedRef) so the body runs only when prevIsConnectedRef.current is
false and isConnected is true; ensure you update prevIsConnectedRef after each
render and call joinTask(selectedTask.id) only when selectedTask exists (the
effect still preserves the “only on reconnect” behavior and avoids lint
suppression).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 559bdeff-ae4a-41bb-8bf7-991aa4b69f9f

📥 Commits

Reviewing files that changed from the base of the PR and between a465998 and 87e86ee.

📒 Files selected for processing (1)
  • frontend/src/features/tasks/contexts/taskContext.tsx

When users click a history task to continue chatting, if WebSocket is not
connected at that moment, the task room will not be joined. This causes
the chat to not work properly because messages are not synced via WebSocket.

Root cause: The original useEffect only handles joinTask when selectedTask
changes. If WebSocket is disconnected when selectedTask is set, joinTask
returns early with 'Not connected' error. When WebSocket reconnects later,
there was no logic to rejoin the task room.

Fix: Add a separate useEffect that monitors isConnected state. When WebSocket
reconnects and there is a selected task, it automatically rejoins the task room.
joinTask has built-in deduplication, so it's safe to call multiple times.

Changes:
- Add useEffect to handle WebSocket reconnection in taskContext.tsx
- When isConnected becomes true and selectedTask exists, rejoin the task room
- Update comments to clarify the logic
@parabala parabala force-pushed the wegent/fix-history-chat-continue branch from 87e86ee to ed2ee0c Compare April 23, 2026 11:27
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx (1)

170-1201: Split this hook before adding more send-path logic.

This file is now 1203 lines, and the two send handlers are large enough that the new team validation was easy to apply inconsistently. Consider extracting payload construction, pending-context building, and validation/toast helpers.

As per coding guidelines, “File size limit: if a file exceeds 1000 lines, split it into multiple sub-modules” and “Maximum function length of 50 lines preferred for all functions”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx` around
lines 170 - 1201, The hook file has grown >1000 lines and the two large send
handlers (handleSendMessage and handleSendMessageWithModel) contain duplicated
logic and inconsistent team/validation/toast behavior; split this file by
extracting: (1) payload construction logic used before contextSendMessage (move
code that builds contextItems, messageWithQueueContent, queueAttachmentIds,
inboxAttachmentContexts, pendingContexts and final request body into a helper
like buildSendPayload), (2) pending-context/attachment mapping into a helper
like buildPendingContexts, and (3) validation/toast checks (team selection, repo
requirement, attachment upload state) into small functions like
validateSendPreconditions and showSendErrorToast; import these helpers into
useChatStreamHandlers and replace the inline blocks in handleSendMessage and
handleSendMessageWithModel so both use the same shared helpers and maintain
identical validation/messages and keep each function short.
frontend/src/features/tasks/components/chat/ChatArea.tsx (1)

91-1309: Consider splitting ChatAreaContent into smaller modules.

This component file is 1309 lines, making subtle state-sync changes harder to reason about. The team-sync effect, generation-mode state, message callbacks, and input props are good extraction candidates.

As per coding guidelines, “File size limit: if a file exceeds 1000 lines, split it into multiple sub-modules” and “Maximum function length of 50 lines preferred for all functions”.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/chat/ChatArea.tsx` around lines 91 -
1309, ChatAreaContent is too large (1309 lines); split responsibilities into
focused sub-modules: extract the team-sync useEffect into a useTeamSync hook
(referencing filteredTeams, selectedTaskDetail, taskIdFromUrl, handleTeamChange,
hasInitializedTeamRef), move video/image generation state and related effects
into useGenerateState (selectedResolution/Ratio/Duration, videoModelSelection,
availableResolutions/ratios/durations, videoConfig, generateParams), move
stream/message callbacks into useChatHandlers (streamHandlers creation and refs:
handleSendMessageRef, handleRetryRef, handleSendMessageWithModelRef,
handleReEdit, handleUseAsReference, handleQueueMessageLoaded), and extract
inputCardProps construction into buildInputCardProps or ChatInputCardContainer
(uses chatState, skillSelector, videoModelSelection/imageModelSelection,
generateParams, streamHandlers); keep exported ChatAreaContent that composes
these hooks/components and only contains top-level JSX and small glue logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/features/tasks/components/chat/ChatArea.tsx`:
- Line 119: The bug is that selectedTeam can remain unset when a user clicks
history because the component waits for selectedTaskDetail?.id; update the
selection logic in ChatArea to fall back to selectedTask.team_id whenever
selectedTask exists and its id equals the task id from the URL (route param) so
the team is synced immediately while details load. Locate the places using
selectedTaskDetail?.id (e.g., the current fallback computation and the blocks
around the earlier mention and lines ~316-327 and ~392-400) and change them to
check for selectedTaskDetail?.id OR (selectedTask && selectedTask.id ===
urlTaskId && selectedTask.team_id) and use selectedTask.team_id to set
selectedTeam/setSelectedTask accordingly.

In `@frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx`:
- Around line 455-463: The model-override/regeneration path is missing the same
team check and can send team_id: 0; update the send handlers to guard against
missing selectedTeam before sending by reusing the existing check logic (toast +
return) used earlier, specifically add the selectedTeam?.id guard in
handleSendMessageWithModel and in the other send/regenerate handler that
constructs messages for model-override (the same code path referenced around the
other send function), ensuring any send always early-returns with the
destructive toast when selectedTeam is absent instead of sending team_id: 0.
- Around line 459-460: The handleSendMessage callback uses the i18n translator
t() for error messages but t is not included in its dependency array, causing
stale translations and an exhaustive-deps lint violation; update the dependency
array of handleSendMessage to include t (the i18n translate function) so the
callback is recreated when the language changes, ensuring lines that call
t('chat:errors.team_not_selected') and
t('chat:errors.team_not_selected_description') use current translations.

---

Nitpick comments:
In `@frontend/src/features/tasks/components/chat/ChatArea.tsx`:
- Around line 91-1309: ChatAreaContent is too large (1309 lines); split
responsibilities into focused sub-modules: extract the team-sync useEffect into
a useTeamSync hook (referencing filteredTeams, selectedTaskDetail,
taskIdFromUrl, handleTeamChange, hasInitializedTeamRef), move video/image
generation state and related effects into useGenerateState
(selectedResolution/Ratio/Duration, videoModelSelection,
availableResolutions/ratios/durations, videoConfig, generateParams), move
stream/message callbacks into useChatHandlers (streamHandlers creation and refs:
handleSendMessageRef, handleRetryRef, handleSendMessageWithModelRef,
handleReEdit, handleUseAsReference, handleQueueMessageLoaded), and extract
inputCardProps construction into buildInputCardProps or ChatInputCardContainer
(uses chatState, skillSelector, videoModelSelection/imageModelSelection,
generateParams, streamHandlers); keep exported ChatAreaContent that composes
these hooks/components and only contains top-level JSX and small glue logic.

In `@frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx`:
- Around line 170-1201: The hook file has grown >1000 lines and the two large
send handlers (handleSendMessage and handleSendMessageWithModel) contain
duplicated logic and inconsistent team/validation/toast behavior; split this
file by extracting: (1) payload construction logic used before
contextSendMessage (move code that builds contextItems, messageWithQueueContent,
queueAttachmentIds, inboxAttachmentContexts, pendingContexts and final request
body into a helper like buildSendPayload), (2) pending-context/attachment
mapping into a helper like buildPendingContexts, and (3) validation/toast checks
(team selection, repo requirement, attachment upload state) into small functions
like validateSendPreconditions and showSendErrorToast; import these helpers into
useChatStreamHandlers and replace the inline blocks in handleSendMessage and
handleSendMessageWithModel so both use the same shared helpers and maintain
identical validation/messages and keep each function short.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c2b5f9d2-0a5d-422a-96e6-1cf4d9dfa094

📥 Commits

Reviewing files that changed from the base of the PR and between 87e86ee and ed2ee0c.

📒 Files selected for processing (5)
  • frontend/src/features/tasks/components/chat/ChatArea.tsx
  • frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx
  • frontend/src/features/tasks/contexts/taskContext.tsx
  • frontend/src/i18n/locales/en/chat.json
  • frontend/src/i18n/locales/zh-CN/chat.json
✅ Files skipped from review due to trivial changes (2)
  • frontend/src/i18n/locales/zh-CN/chat.json
  • frontend/src/i18n/locales/en/chat.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/features/tasks/contexts/taskContext.tsx


// Task context
const { selectedTaskDetail, setSelectedTask, accessDenied } = useTaskContext()
const { selectedTask, selectedTaskDetail, setSelectedTask, accessDenied } = useTaskContext()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use selectedTask.team_id before task detail resolves.

The fallback is computed, but Case 1 still requires selectedTaskDetail?.id, so a history click can leave selectedTeam unset/old until detail loading finishes. Sync from selectedTask when it matches the URL task ID.

Proposed fix
     const detailTeamId = selectedTaskDetail?.team
       ? typeof selectedTaskDetail.team === 'number'
         ? selectedTaskDetail.team
         : (selectedTaskDetail.team as Team).id
-      : selectedTask?.team_id || null
+      : null
+
+    const selectedTaskMatchesUrl =
+      taskIdFromUrl && selectedTask?.id.toString() === taskIdFromUrl
+
+    if (selectedTaskMatchesUrl && selectedTask.team_id) {
+      const teamFromTask = filteredTeams.find(t => t.id === selectedTask.team_id)
+      if (
+        teamFromTask &&
+        (lastSyncedTaskIdRef.current !== selectedTask.id ||
+          selectedTeam?.id !== selectedTask.team_id)
+      ) {
+        handleTeamChange(teamFromTask)
+        lastSyncedTaskIdRef.current = selectedTask.id
+        hasInitializedTeamRef.current = true
+        return
+      }
+    }
 
     // Case 1: Sync from task detail (HIGHEST PRIORITY)

Also applies to: 316-327, 392-400

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/chat/ChatArea.tsx` at line 119, The
bug is that selectedTeam can remain unset when a user clicks history because the
component waits for selectedTaskDetail?.id; update the selection logic in
ChatArea to fall back to selectedTask.team_id whenever selectedTask exists and
its id equals the task id from the URL (route param) so the team is synced
immediately while details load. Locate the places using selectedTaskDetail?.id
(e.g., the current fallback computation and the blocks around the earlier
mention and lines ~316-327 and ~392-400) and change them to check for
selectedTaskDetail?.id OR (selectedTask && selectedTask.id === urlTaskId &&
selectedTask.team_id) and use selectedTask.team_id to set
selectedTeam/setSelectedTask accordingly.

Comment on lines +455 to +463
// Check if team is selected before sending
if (!selectedTeam?.id) {
toast({
variant: 'destructive',
title: t('chat:errors.team_not_selected') || '请选择智能体',
description: t('chat:errors.team_not_selected_description') || '请从列表中选择一个智能体后开始对话',
})
return
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Apply the team guard to model-override sends too.

handleSendMessageWithModel can still send team_id: 0 when selectedTeam is missing, so regeneration/model-override sends can bypass the new protection.

Proposed fix
       const message = overrideMessage.trim()
       if (!message && !shouldHideChatInput) return
 
+      if (!selectedTeam?.id) {
+        toast({
+          variant: 'destructive',
+          title: t('chat:errors.team_not_selected') || '请选择智能体',
+          description:
+            t('chat:errors.team_not_selected_description') ||
+            '请从列表中选择一个智能体后开始对话',
+        })
+        return
+      }
+
       if (!isAttachmentReadyToSend) {
         toast({
           variant: 'destructive',
           title: t('chat:upload.wait_for_upload'),
@@
-            team_id: selectedTeam?.id ?? 0,
+            team_id: selectedTeam.id,

Also applies to: 938-942

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx` around
lines 455 - 463, The model-override/regeneration path is missing the same team
check and can send team_id: 0; update the send handlers to guard against missing
selectedTeam before sending by reusing the existing check logic (toast + return)
used earlier, specifically add the selectedTeam?.id guard in
handleSendMessageWithModel and in the other send/regenerate handler that
constructs messages for model-override (the same code path referenced around the
other send function), ensuring any send always early-returns with the
destructive toast when selectedTeam is absent instead of sending team_id: 0.

Comment on lines +459 to +460
title: t('chat:errors.team_not_selected') || '请选择智能体',
description: t('chat:errors.team_not_selected_description') || '请从列表中选择一个智能体后开始对话',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

fd -t f "useChatStreamHandlers.tsx"

Repository: wecode-ai/Wegent

Length of output: 129


🏁 Script executed:

head -n 850 frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | tail -n +450 | head -n 50

Repository: wecode-ai/Wegent

Length of output: 1595


🏁 Script executed:

grep -n "handleSendMessage" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | head -20

Repository: wecode-ai/Wegent

Length of output: 728


🏁 Script executed:

# Get the full context around lines 459-460 and dependency array
sed -n '450,470p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 736


🏁 Script executed:

# Look for the useCallback/useMemo hook that defines handleSendMessage and its dependencies
ast-grep --pattern 'const handleSendMessage = useCallback($$$, [$$$])'

Repository: wecode-ai/Wegent

Length of output: 40050


🏁 Script executed:

head -n 100 frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | grep -E "import|useTranslation|useUnifiedMessages"

Repository: wecode-ai/Wegent

Length of output: 1284


🏁 Script executed:

# Search for all toast calls in the file to see if this is a pattern
grep -n "toast({" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | head -20

Repository: wecode-ai/Wegent

Length of output: 325


🏁 Script executed:

# Check if useUnifiedMessages is used elsewhere in this file
grep -n "useUnifiedMessages\|messages" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | head -20

Repository: wecode-ai/Wegent

Length of output: 685


🏁 Script executed:

# Get the full import statement for useTranslation
sed -n '1,50p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | grep -A 5 "useTranslation"

Repository: wecode-ai/Wegent

Length of output: 422


🏁 Script executed:

# Check how t is used - find the destructuring or usage
grep -n "const.*=.*useTranslation\|const.*t.*=" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | head -5

Repository: wecode-ai/Wegent

Length of output: 248


🏁 Script executed:

# Search for where t is actually initialized in the hook
sed -n '100,200p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 3029


🏁 Script executed:

# Verify if there are other useCallback hooks with t() usage in the file
grep -B 10 "t('chat:" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | grep -E "useCallback|t\(" | head -20

Repository: wecode-ai/Wegent

Length of output: 610


🏁 Script executed:

# Check the specific lines 764-804 to see the full dependency array
sed -n '764,804p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 977


🏁 Script executed:

# Check if there are ESLint overrides or if react-hooks/exhaustive-deps would catch this
grep -r "exhaustive-deps\|eslint-disable" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 42


🏁 Script executed:

# Check other useCallback hooks to see if they also have the same issue with t
grep -B 5 -A 50 "const handleRetry = useCallback" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | grep -E "useCallback|t\(|^\s*\[" | head -30

Repository: wecode-ai/Wegent

Length of output: 299


🏁 Script executed:

# Check for eslint config and react-hooks/exhaustive-deps settings
fd -t f ".eslintrc*|eslint.config.*" frontend/

Repository: wecode-ai/Wegent

Length of output: 86


🏁 Script executed:

# Let's see if there's a package.json with eslint setup
head -n 100 package.json | grep -A 10 "eslint"

Repository: wecode-ai/Wegent

Length of output: 131


🏁 Script executed:

cat frontend/eslint.config.mjs

Repository: wecode-ai/Wegent

Length of output: 1016


🏁 Script executed:

# Look for handleSendMessageWithModel too, which was mentioned in the "Also applies to" comment
grep -B 5 -A 50 "const handleSendMessageWithModel = useCallback" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | head -80

Repository: wecode-ai/Wegent

Length of output: 2008


🏁 Script executed:

# Find the complete handleSendMessageWithModel dependency array
sed -n '814,1050p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | grep -n -A 50 "^\s*\[" | head -60

Repository: wecode-ai/Wegent

Length of output: 2110


🏁 Script executed:

# Get the complete handleSendMessageWithModel dependency array more precisely
sed -n '1000,1050p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 1189


🏁 Script executed:

# Find createRetryButton useCallback and its dependency array
grep -n "createRetryButton" frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 205


🏁 Script executed:

# Get the context around createRetryButton
sed -n '390,430p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 1510


🏁 Script executed:

# Double-check handleSendMessage uses t() at other places too
sed -n '450,763p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx | grep -n "t('chat:" | head -10

Repository: wecode-ai/Wegent

Length of output: 224


🏁 Script executed:

# Verify the full context - where does handleSendMessage end and what's at 764-804
sed -n '800,810p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 498


🏁 Script executed:

# Confirm the exact line where handleSendMessage dependency array starts and ends
sed -n '760,806p' frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx

Repository: wecode-ai/Wegent

Length of output: 1029


Add t to the handleSendMessage dependencies.

The callback uses t() for translating error messages at lines 459-460, but t is missing from the dependency array, which will cause stale translations after language changes and violates react-hooks/exhaustive-deps.

Proposed fix
       effectiveDeviceId,
       effectiveRequiresWorkspace,
       additionalSkills,
+      t,
       generateParams,
     ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
title: t('chat:errors.team_not_selected') || '请选择智能体',
description: t('chat:errors.team_not_selected_description') || '请从列表中选择一个智能体后开始对话',
effectiveDeviceId,
effectiveRequiresWorkspace,
additionalSkills,
t,
generateParams,
]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/features/tasks/components/chat/useChatStreamHandlers.tsx` around
lines 459 - 460, The handleSendMessage callback uses the i18n translator t() for
error messages but t is not included in its dependency array, causing stale
translations and an exhaustive-deps lint violation; update the dependency array
of handleSendMessage to include t (the i18n translate function) so the callback
is recreated when the language changes, ensuring lines that call
t('chat:errors.team_not_selected') and
t('chat:errors.team_not_selected_description') use current translations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant