From 49d31d084705b948d83049952a6d980484a078ad Mon Sep 17 00:00:00 2001 From: matevip Date: Thu, 13 Aug 2026 03:20:39 -0400 Subject: [PATCH] fix(chat): keep route agent for conversation deep links --- mateclaw-ui/src/utils/chatRouteHydration.ts | 11 +++++++++ mateclaw-ui/src/views/ChatConsole.vue | 12 ++++++---- .../chatConsoleRouteHydration.test.ts | 24 ++++++++++++++++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/mateclaw-ui/src/utils/chatRouteHydration.ts b/mateclaw-ui/src/utils/chatRouteHydration.ts index 78c17eee..4b8f04d0 100644 --- a/mateclaw-ui/src/utils/chatRouteHydration.ts +++ b/mateclaw-ui/src/utils/chatRouteHydration.ts @@ -23,3 +23,14 @@ export function resolveRouteHydrationQuery(options: { return { agentId, conversationId } } + +export function resolveConversationAgentSelection(options: { + routeAgentId?: string + conversationAgentId?: IdLike | null + currentAgentId?: IdLike | null +}): string { + if (options.routeAgentId) return options.routeAgentId + if (options.conversationAgentId != null) return String(options.conversationAgentId) + if (options.currentAgentId != null) return String(options.currentAgentId) + return '' +} diff --git a/mateclaw-ui/src/views/ChatConsole.vue b/mateclaw-ui/src/views/ChatConsole.vue index 5c2ad122..8c34025e 100644 --- a/mateclaw-ui/src/views/ChatConsole.vue +++ b/mateclaw-ui/src/views/ChatConsole.vue @@ -291,7 +291,7 @@ import { useChat } from '@/composables/chat/useChat' import RunOverviewPanel from '@/components/chat/RunOverviewPanel.vue' import { reconstructErrorInfo } from '@/types/chatError' import { reconcileMessages, extractMessages } from '@/utils/messageReconcile' -import { resolveRouteHydrationQuery } from '@/utils/chatRouteHydration' +import { resolveConversationAgentSelection, resolveRouteHydrationQuery } from '@/utils/chatRouteHydration' import type { Conversation, Agent, ModelConfig, ProviderInfo, ActiveModelsInfo, ChatAttachment, MessageContentPart, Message, ToolCallMeta } from '@/types' // 导入组件化组件 @@ -1632,7 +1632,7 @@ async function hydrateStateFromRoute() { if (conversationId && conversationId !== currentConversationId.value) { const matchedConversation = conversations.value.find(conv => conv.conversationId === conversationId) if (matchedConversation) { - await selectConversation(matchedConversation) + await selectConversation(matchedConversation, agentId) } else { // 会话不在已加载列表中(可能来自 Sessions 页面跳转),尝试加载消息 currentConversationId.value = conversationId @@ -1669,7 +1669,7 @@ function syncRouteState() { router.replace({ path: '/chat', query }) } -async function selectConversation(conv: Conversation) { +async function selectConversation(conv: Conversation, routeAgentId = '') { if (isMobile.value) convPanelOpen.value = false // 切换到不同会话:只清理本地 UI/SSE(resetForNewConversation 会 stream.disconnect + 清变量), // 但不 POST /chat/{A}/stop —— 让 A 的后台 agent run 跑到完成。 @@ -1682,7 +1682,11 @@ async function selectConversation(conv: Conversation) { messageListRef.value?.resetScrollLock() } currentConversationId.value = conv.conversationId - selectedAgentId.value = conv.agentId || selectedAgentId.value + selectedAgentId.value = resolveConversationAgentSelection({ + routeAgentId, + conversationAgentId: conv.agentId, + currentAgentId: selectedAgentId.value, + }) // Opening another conversation: its pin (or the agent/global fallback) is // authoritative, so clear the previous conversation's manual-pick guard. userPickedModel.value = false diff --git a/mateclaw-ui/src/views/__tests__/chatConsoleRouteHydration.test.ts b/mateclaw-ui/src/views/__tests__/chatConsoleRouteHydration.test.ts index 2ef202bb..a31929b2 100644 --- a/mateclaw-ui/src/views/__tests__/chatConsoleRouteHydration.test.ts +++ b/mateclaw-ui/src/views/__tests__/chatConsoleRouteHydration.test.ts @@ -1,6 +1,6 @@ // @vitest-environment happy-dom import { describe, expect, it } from 'vitest' -import { resolveRouteHydrationQuery } from '@/utils/chatRouteHydration' +import { resolveConversationAgentSelection, resolveRouteHydrationQuery } from '@/utils/chatRouteHydration' const agents = [{ id: 'agent-visible' }] const conversations = [{ conversationId: 'conv-listed' }] @@ -34,3 +34,25 @@ describe('resolveRouteHydrationQuery', () => { }) }) }) + +describe('resolveConversationAgentSelection', () => { + it('keeps a valid route agent when opening an existing conversation with stale metadata', () => { + const selected = resolveConversationAgentSelection({ + routeAgentId: '20798621241343139868', + conversationAgentId: '2079862124313986', + currentAgentId: '', + }) + + expect(selected).toBe('20798621241343139868') + }) + + it('uses the conversation agent when no route agent is provided', () => { + const selected = resolveConversationAgentSelection({ + routeAgentId: '', + conversationAgentId: '2079862124313986', + currentAgentId: 'fallback', + }) + + expect(selected).toBe('2079862124313986') + }) +})