diff --git a/static/src/app/methods/taskPolling/compression.ts b/static/src/app/methods/taskPolling/compression.ts index 24e3c5ef..4c188971 100644 --- a/static/src/app/methods/taskPolling/compression.ts +++ b/static/src/app/methods/taskPolling/compression.ts @@ -112,6 +112,23 @@ export const compressionMethods = { messagesLen: Array.isArray(this.messages) ? this.messages.length : -1 }); + // 显式新建对话路由(/new、/multiagent/new):运行中的任务属于上一个对话, + // 用户主动选择了空白页。这里不能接管任务/重放事件/恢复轮询—— + // loadRunningTask(null) 会匹配任意运行中任务,一旦接管,该对话的 + // 用户输入/AI输出/工具调用会被全量重放到空白页(刷新也复现)。 + // 侧边栏运行标识由 refreshRunningWorkspaceTasks 独立维护,不受影响。 + if ( + !this.currentConversationId && + typeof this.isExplicitNewConversationRoute === 'function' && + this.isExplicitNewConversationRoute() + ) { + restoreDebugLog('restore:skip-explicit-new-route', { + path: typeof window !== 'undefined' ? window.location.pathname : '' + }); + this._restoreTaskStateWaitCount = 0; + return; + } + try { const { useTaskStore } = await import('../../../stores/task'); const taskStore = useTaskStore(); diff --git a/static/src/app/methods/taskPolling/lifecycle.ts b/static/src/app/methods/taskPolling/lifecycle.ts index f7550825..3442d916 100644 --- a/static/src/app/methods/taskPolling/lifecycle.ts +++ b/static/src/app/methods/taskPolling/lifecycle.ts @@ -70,12 +70,20 @@ export const lifecycleMethods = { 'shallow_compression', 'compression_finished' ]); - if ( - !crossConversationAllowed.has(eventType) && + // 显式新建路由(/new、/multiagent/new)上 currentConversationId 为空是常态: + // 空 id 不等于“无归属”,此时任何携带 conversation_id 的事件都属于其他对话, + // 必须丢弃,否则运行中对话的用户输入/AI输出/工具调用会渲染到空白页。 + const onExplicitNewRoute = + typeof this.isExplicitNewConversationRoute === 'function' && + this.isExplicitNewConversationRoute(); + const eventConversationMismatch = !!( eventData.conversation_id && - this.currentConversationId - ) { - if (eventData.conversation_id !== this.currentConversationId) { + (this.currentConversationId + ? eventData.conversation_id !== this.currentConversationId + : onExplicitNewRoute) + ); + if (!crossConversationAllowed.has(eventType) && eventConversationMismatch) { + { if ( [ 'ai_message_start', diff --git a/static/src/composables/useLegacySocket.ts b/static/src/composables/useLegacySocket.ts index 635bd0ed..8f94572c 100644 --- a/static/src/composables/useLegacySocket.ts +++ b/static/src/composables/useLegacySocket.ts @@ -1098,7 +1098,12 @@ export async function initializeLegacySocket(ctx: any) { }); // 思考流开始 - ctx.socket.on('thinking_start', () => { + ctx.socket.on('thinking_start', (data) => { + // 对话定向事件:只响应当前对话,避免运行中对话的流式输出渲染到 /new 等空白页。 + if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) { + socketLog('跳过thinking_start(对话不匹配)', data.conversation_id); + return; + } // 轮询模式下跳过 WebSocket 事件 if (ctx.usePollingMode && !ctx.waitingForSubAgent) { socketLog('思考开始 (轮询模式,跳过)'); @@ -1125,6 +1130,10 @@ export async function initializeLegacySocket(ctx: any) { // 思考内容块 ctx.socket.on('thinking_chunk', (data) => { + // 对话定向事件:只响应当前对话。 + if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) { + return; + } // 轮询模式下跳过 WebSocket 事件 if (ctx.usePollingMode && !ctx.waitingForSubAgent) { return; @@ -1147,6 +1156,11 @@ export async function initializeLegacySocket(ctx: any) { // 思考结束 ctx.socket.on('thinking_end', (data) => { + // 对话定向事件:只响应当前对话。 + if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) { + socketLog('跳过thinking_end(对话不匹配)', data.conversation_id); + return; + } // 轮询模式下跳过 WebSocket 事件 if (ctx.usePollingMode && !ctx.waitingForSubAgent) { socketLog('思考结束 (轮询模式,跳过)'); @@ -1171,7 +1185,12 @@ export async function initializeLegacySocket(ctx: any) { }); // 文本流开始 - ctx.socket.on('text_start', () => { + ctx.socket.on('text_start', (data) => { + // 对话定向事件:只响应当前对话。 + if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) { + socketLog('跳过text_start(对话不匹配)', data.conversation_id); + return; + } // 轮询模式下跳过 WebSocket 事件 if (ctx.usePollingMode && !ctx.waitingForSubAgent) { socketLog('文本开始 (轮询模式,跳过)'); @@ -1191,6 +1210,10 @@ export async function initializeLegacySocket(ctx: any) { // 文本内容块 ctx.socket.on('text_chunk', (data) => { + // 对话定向事件:只响应当前对话。 + if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) { + return; + } // 轮询模式下跳过 WebSocket 事件 if (ctx.usePollingMode && !ctx.waitingForSubAgent) { return; @@ -1228,6 +1251,11 @@ export async function initializeLegacySocket(ctx: any) { // 文本结束 ctx.socket.on('text_end', (data) => { + // 对话定向事件:只响应当前对话。 + if (data?.conversation_id && data.conversation_id !== ctx.currentConversationId) { + socketLog('跳过text_end(对话不匹配)', data.conversation_id); + return; + } // 轮询模式下跳过 WebSocket 事件 if (ctx.usePollingMode && !ctx.waitingForSubAgent) { socketLog('文本结束 (轮询模式,跳过)');