diff --git a/server/chat_flow_task_main.py b/server/chat_flow_task_main.py index e01b0b6..e9c9186 100644 --- a/server/chat_flow_task_main.py +++ b/server/chat_flow_task_main.py @@ -1833,9 +1833,43 @@ async def handle_task_with_sender( ) except Exception as exc: debug_log(f"[Goal] 用户停止时停止目标失败: {exc}") + # 计算后台任务状态,前端用这个字段决定是否保持对话运行态、输入区是否释放 + _stopped_has_bg = False + _stopped_has_bg_cmd = False + _stopped_has_ma = False + _manager = getattr(web_terminal, "sub_agent_manager", None) + if _manager: + try: + _manager.reconcile_task_states(conversation_id=conversation_id) + except Exception: + pass + if getattr(web_terminal, "multi_agent_mode", False): + _state = _manager.get_multi_agent_state(conversation_id) + if _state: + _stopped_has_ma = any(a.status == "running" for a in _state.list_all()) + for _t in _manager.tasks.values(): + if _t.get("conversation_id") != conversation_id: + continue + if _t.get("status") in TERMINAL_STATUSES.union({"terminated"}): + continue + if _t.get("multi_agent_mode"): + continue + _stopped_has_bg = True + break + _bg_mgr = getattr(web_terminal, "background_command_manager", None) + if _bg_mgr and conversation_id: + try: + _bg_mgr.reconcile_stale_records(conversation_id=conversation_id) + except Exception: + pass + if _bg_mgr.list_waiting_items(conversation_id): + _stopped_has_bg_cmd = True sender('task_stopped', { 'message': '任务已停止', - 'reason': 'user_requested' + 'reason': 'user_requested', + 'has_running_sub_agents': _stopped_has_bg, + 'has_running_background_commands': _stopped_has_bg_cmd, + 'has_running_multi_agent': _stopped_has_ma, }) break @@ -2189,7 +2223,10 @@ async def handle_task_with_sender( sender('task_stopped', { 'message': tool_loop_result.get("approval_message") or '操作被用户拒绝', 'reason': 'approval_rejected', - 'conversation_id': conversation_id + 'conversation_id': conversation_id, + 'has_running_sub_agents': False, + 'has_running_background_commands': False, + 'has_running_multi_agent': False }) finalize_user_work_timer() finalize_run_versioning_checkpoint("approval_rejected") diff --git a/static/src/App.vue b/static/src/App.vue index 48746e6..da38da0 100644 --- a/static/src/App.vue +++ b/static/src/App.vue @@ -279,7 +279,7 @@ :input-is-multiline="inputIsMultiline" :input-is-focused="inputIsFocused" :is-connected="isConnected" - :streaming-message="composerStreamingForInput" + :streaming-message="streamingUi" :input-locked="displayLockEngaged" :uploading="uploading" :media-uploading="mediaUploading" diff --git a/static/src/app/methods/taskPolling/lifecycle.ts b/static/src/app/methods/taskPolling/lifecycle.ts index bc6011d..49e788e 100644 --- a/static/src/app/methods/taskPolling/lifecycle.ts +++ b/static/src/app/methods/taskPolling/lifecycle.ts @@ -486,22 +486,13 @@ export const lifecycleMethods = { this.stopRequested = false; if (hasRunningBackground) { - // 智能体已停,但后台任务还在跑:保持停止按钮,等用户第二下清理 - // 此时对话并未真正停止,work_timer 保持运行,不提前标记完成 + // 主智能体已停,后台任务仍在跑:保持 taskInProgress=true (对话列表显示运行中), + // 但输入栏不再锁定(stopRequested 已重置),用户可发新消息触发下一轮。 + // 后台任务的停止由独立的子智能体/后台指令按钮处理,不再走停止按钮二次点击。 this.taskInProgress = true; this.waitingForSubAgent = hasRunningSubAgents; this.waitingForBackgroundCommand = hasRunningBackgroundCommands; - debugLog('[TaskPolling] 任务已停止,仍有后台任务运行,保持停止态'); - goalModeDebugLog('handleTaskStopped:show-toast', { hasRunningSubAgents, hasRunningBackgroundCommands }); - try { - this.uiPushToast({ - title: '智能体已停止', - message: '再次按下停止按钮以结束后台任务', - type: 'info' - }); - } catch (err) { - console.warn('[TaskPolling] uiPushToast 调用失败:', err); - } + debugLog('[TaskPolling] 任务已停止,仍有后台任务运行,保持对话运行态但释放输入区'); } else { // 对话真正停止:停止计时器并持久化 this.markLatestUserWorkCompleted();