fix(chat): preserve team task deep links (#596)

This commit is contained in:
matevip 2026-08-13 02:47:33 -04:00
parent ba0e4506b7
commit 4d9e7b024f
3 changed files with 68 additions and 18 deletions

View File

@ -0,0 +1,25 @@
type IdLike = string | number
interface RouteHydrationAgent {
id: IdLike
}
interface RouteHydrationConversation {
conversationId: string
}
export function resolveRouteHydrationQuery(options: {
routeAgentId?: string
routeConversationId?: string
agents: RouteHydrationAgent[]
conversations: RouteHydrationConversation[]
}): { agentId: string; conversationId: string } {
let agentId = options.routeAgentId || ''
const conversationId = options.routeConversationId || ''
if (agentId && options.agents.length > 0 && !options.agents.some(a => String(a.id) === agentId)) {
agentId = ''
}
return { agentId, conversationId }
}

View File

@ -291,6 +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 type { Conversation, Agent, ModelConfig, ProviderInfo, ActiveModelsInfo, ChatAttachment, MessageContentPart, Message, ToolCallMeta } from '@/types'
//
@ -1617,24 +1618,12 @@ async function refreshCurrentConversationMessages(conversationId: string) {
}
async function hydrateStateFromRoute() {
let agentId = route.query.agentId ? String(route.query.agentId) : ''
let conversationId = String(route.query.conversationId || '')
// The URL can outlive its workspace: switching workspaces remounts this view
// (via the router-view key) but keeps the query string, so agentId /
// conversationId may still point at entities of the previous workspace. An
// agentId missing from the workspace-scoped agent list is such a leftover
// drop it so the default-select below picks a real employee instead of the
// picker rendering the unresolvable raw id.
if (agentId && agents.value.length > 0 && !agents.value.some(a => String(a.id) === agentId)) {
agentId = ''
// Only follow the paired conversationId when it resolves locally (e.g. a
// Sessions-page jump within this workspace); otherwise it is equally stale
// and would attach the fallback agent to a foreign conversation.
if (!conversations.value.some(conv => conv.conversationId === conversationId)) {
conversationId = ''
}
}
const { agentId, conversationId } = resolveRouteHydrationQuery({
routeAgentId: route.query.agentId ? String(route.query.agentId) : '',
routeConversationId: String(route.query.conversationId || ''),
agents: agents.value,
conversations: conversations.value,
})
if (agentId && agentId !== String(selectedAgentId.value)) {
selectedAgentId.value = agentId

View File

@ -0,0 +1,36 @@
// @vitest-environment happy-dom
import { describe, expect, it } from 'vitest'
import { resolveRouteHydrationQuery } from '@/utils/chatRouteHydration'
const agents = [{ id: 'agent-visible' }]
const conversations = [{ conversationId: 'conv-listed' }]
describe('resolveRouteHydrationQuery', () => {
it('keeps a deep-linked child conversation even when it is not in the sidebar list', () => {
const result = resolveRouteHydrationQuery({
routeAgentId: 'agent-deleted-or-hidden',
routeConversationId: 'team-task-finished',
agents,
conversations,
})
expect(result).toEqual({
agentId: '',
conversationId: 'team-task-finished',
})
})
it('keeps valid route agent and conversation ids unchanged', () => {
const result = resolveRouteHydrationQuery({
routeAgentId: 'agent-visible',
routeConversationId: 'conv-listed',
agents,
conversations,
})
expect(result).toEqual({
agentId: 'agent-visible',
conversationId: 'conv-listed',
})
})
})