diff --git a/static/src/app/methods/history.ts b/static/src/app/methods/history.ts index e5c0d88..46fbf4c 100644 --- a/static/src/app/methods/history.ts +++ b/static/src/app/methods/history.ts @@ -26,6 +26,18 @@ function parseSystemNoticeLabel(rawContent: any): string | null { return parseSubAgentDoneLabel(rawContent) || parseBackgroundRunCommandDoneLabel(rawContent); } +function isSystemAutoUserMessageMeta(meta: any): boolean { + if (!meta || typeof meta !== 'object') { + return false; + } + return !!( + meta.is_auto_generated || + meta.auto_message_type || + meta.sub_agent_notice || + meta.background_command_notice + ); +} + export const historyMethods = { // ========================================== // 关键功能:获取并显示历史对话内容 @@ -199,6 +211,14 @@ export const historyMethods = { debugLog('跳过系统代发的图片/视频消息(仅用于模型查看,不在前端展示)'); return; } + if (message.role === 'user' && isSystemAutoUserMessageMeta(meta)) { + debugLog('跳过系统自动代发的 user 消息(前端不渲染)', { + auto_message_type: meta.auto_message_type || null, + sub_agent_notice: !!meta.sub_agent_notice, + background_command_notice: !!meta.background_command_notice + }); + return; + } if (message.role === 'user') { // 用户消息 - 先结束之前的assistant消息 diff --git a/static/src/app/methods/taskPolling.ts b/static/src/app/methods/taskPolling.ts index 78bbac1..5fc9fbe 100644 --- a/static/src/app/methods/taskPolling.ts +++ b/static/src/app/methods/taskPolling.ts @@ -10,12 +10,60 @@ const keyNotifyLog = (...args: any[]) => { const jsonDebug = (...args: any[]) => { console.log('[JSONDEBUG]', ...args); }; +const userMDebug = (...args: any[]) => { + console.log('[USERMDEBUG]', ...args); +}; + +function isSystemAutoUserMessagePayload(data: any): boolean { + if (!data || typeof data !== 'object') { + return false; + } + const meta = data.metadata || {}; + return !!( + data.is_auto_generated || + meta.is_auto_generated || + data.auto_message_type || + meta.auto_message_type || + data.sub_agent_notice || + meta.sub_agent_notice || + data.background_command_notice || + meta.background_command_notice + ); +} + +function isEmptyAssistantPlaceholderMessage(message: any): boolean { + if (!message || message.role !== 'assistant') { + return false; + } + const actions = Array.isArray(message.actions) ? message.actions : []; + return actions.length === 0 && !!message.awaitingFirstContent; +} /** * 任务轮询事件处理器 * 将从 REST API 轮询获取的事件转换为前端状态更新 */ export const taskPollingMethods = { + cleanupTrailingEmptyAssistantPlaceholder(reason = 'unspecified') { + if (!Array.isArray(this.messages) || !this.messages.length) { + return false; + } + const last = this.messages[this.messages.length - 1]; + if (!isEmptyAssistantPlaceholderMessage(last)) { + return false; + } + this.messages.pop(); + if (typeof this.currentMessageIndex === 'number') { + this.currentMessageIndex = this.messages.length - 1; + } + userMDebug('taskPolling.cleanupTrailingEmptyAssistantPlaceholder:removed', { + reason, + messagesLengthAfter: this.messages.length, + currentMessageIndex: this.currentMessageIndex + }); + return true; + }, + stopWaitingTaskProbe() { if (this.waitingTaskProbeTimer) { debugNotifyLog('[DEBUG_NOTIFY][ui] stopWaitingTaskProbe'); @@ -369,12 +417,34 @@ export const taskPollingMethods = { // 判断是否是刷新恢复的情况: // 1. 有assistant消息 - // 2. 且该消息正在streaming或者没有任何内容(说明是刚创建的) - // 3. 且不是历史加载完成的消息(历史消息的awaitingFirstContent应该是false) + // 2. 且最后一条 assistant 仍处于“未完成流式状态”(而不是普通已完成消息) + // 3. 且任务确实仍在进行中 + const lastActions = Array.isArray(lastMessage?.actions) ? lastMessage.actions : []; + const hasUnfinishedAction = lastActions.some((action: any) => { + if (!action) return false; + if (action.streaming) return true; + if (action.type === 'tool' && action.tool) { + const status = String(action.tool.status || '').toLowerCase(); + return ['preparing', 'running', 'pending', 'queued', 'awaiting_approval'].includes(status); + } + return false; + }); + const hasNoActionsYet = !lastActions.length; const isRefreshRestore = hasAssistantMessage && - (this.streamingMessage || !lastMessage.actions || lastMessage.actions.length === 0) && - (lastMessage.awaitingFirstContent === true || this.taskInProgress); + (hasNoActionsYet || hasUnfinishedAction || lastMessage.awaitingFirstContent === true) && + (this.taskInProgress || this.streamingMessage); + + userMDebug('taskPolling.handleAiMessageStart:decision', { + eventIdx, + hasAssistantMessage, + hasNoActionsYet, + hasUnfinishedAction, + awaitingFirstContent: !!lastMessage?.awaitingFirstContent, + taskInProgress: this.taskInProgress, + streamingMessage: this.streamingMessage, + isRefreshRestore + }); console.log('[AiMessageStart] 场景判断', { isRefreshRestore, @@ -942,6 +1012,7 @@ export const taskPollingMethods = { this.clearProcessedEvents(); this.startWaitingTaskProbe(); } else { + this.cleanupTrailingEmptyAssistantPlaceholder('task_complete'); // 主任务已结束:若有遗留工具块处于 running/preparing,会导致发送按钮继续显示“停止”。 // 这里统一清理遗留中的工具状态,避免前端忙碌态卡死。 if (typeof this.clearPendingTools === 'function') { @@ -988,6 +1059,7 @@ export const taskPollingMethods = { debugLog('[TaskPolling] 任务已停止, idx:', eventIdx, data); this.markLatestUserWorkCompleted(); + this.cleanupTrailingEmptyAssistantPlaceholder('task_stopped'); this.streamingMessage = false; this.taskInProgress = false; this.stopRequested = false; @@ -1043,10 +1115,18 @@ export const taskPollingMethods = { sub_agent_notice: !!data?.sub_agent_notice, background_command_notice: !!data?.background_command_notice }); - this.chatAddUserMessage(message, data?.images || [], data?.videos || []); - this.taskInProgress = true; - this.streamingMessage = false; - this.stopRequested = false; + const isAutoUserMessage = isSystemAutoUserMessagePayload(data); + if (!isAutoUserMessage) { + this.chatAddUserMessage(message, data?.images || [], data?.videos || []); + this.taskInProgress = true; + this.streamingMessage = false; + this.stopRequested = false; + } else { + debugLog('[TaskPolling] 跳过自动代发 user_message 渲染', { + messagePreview: message.slice(0, 80), + data + }); + } if (data?.sub_agent_notice) { if (typeof data?.has_running_sub_agents === 'boolean') { this.waitingForSubAgent = data.has_running_sub_agents; @@ -1070,16 +1150,23 @@ export const taskPollingMethods = { handleSystemMessage(data: any) { const content = (data?.content || data?.message || '').trim(); - console.log('[DEBUG_SYSTEM][polling] 收到 system_message 事件', { + userMDebug('taskPolling.handleSystemMessage:incoming', { raw: data, normalizedContent: content, - currentConversationId: this.currentConversationId + currentConversationId: this.currentConversationId, + currentMessagesLength: Array.isArray(this.messages) ? this.messages.length : -1 }); if (!content) { - console.log('[DEBUG_SYSTEM][polling] system_message 内容为空,跳过'); + userMDebug('taskPolling.handleSystemMessage:empty-skip'); return; } + this.cleanupTrailingEmptyAssistantPlaceholder('before_system_message_append'); this.appendSystemAction(content); + userMDebug('taskPolling.handleSystemMessage:after-append', { + currentMessagesLength: Array.isArray(this.messages) ? this.messages.length : -1, + lastRole: this.messages?.[this.messages.length - 1]?.role || null, + lastActions: this.messages?.[this.messages.length - 1]?.actions?.map((a: any) => a?.type) || [] + }); }, handleTaskError(data: any) { diff --git a/static/src/app/methods/ui.ts b/static/src/app/methods/ui.ts index bc525a0..0841658 100644 --- a/static/src/app/methods/ui.ts +++ b/static/src/app/methods/ui.ts @@ -19,6 +19,9 @@ import { debugLog } from './common'; const SUB_AGENT_DONE_PREFIX_RE = /^(?:✅\s*)?子智能体\s*#?\s*(\d+)\s*任务摘要[::]/; const BG_RUN_COMMAND_DONE_PREFIX_RE = /^\[后台\s*run_command\s*完成\]/; +const userMDebug = (...args: any[]) => { + console.log('[USERMDEBUG]', ...args); +}; function parseSubAgentDoneLabel(rawContent: any): string | null { const content = (rawContent || '').toString().trim(); @@ -1114,14 +1117,14 @@ export const uiMethods = { }, addSystemMessage(content) { - console.log('[DEBUG_SYSTEM][ui] addSystemMessage 入参', { content }); + userMDebug('ui.addSystemMessage:incoming', { content }); const systemNoticeLabel = parseSystemNoticeLabel(content); if (!systemNoticeLabel) { // 其他 system 消息全部隐藏,且不进入渲染链路,避免出现空白块 - console.log('[DEBUG_SYSTEM][ui] addSystemMessage 被拦截(非完成通知)', { content }); + userMDebug('ui.addSystemMessage:blocked', { content }); return; } - console.log('[DEBUG_SYSTEM][ui] addSystemMessage 通过,写入 action', { + userMDebug('ui.addSystemMessage:accepted', { original: content, normalizedLabel: systemNoticeLabel }); diff --git a/static/src/components/chat/ChatArea.vue b/static/src/components/chat/ChatArea.vue index 9b5e201..df65b1b 100644 --- a/static/src/components/chat/ChatArea.vue +++ b/static/src/components/chat/ChatArea.vue @@ -579,7 +579,7 @@