From cba11ea168348fed61be867dd339204be404bc2f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Jul 2026 23:28:59 +0000 Subject: [PATCH] fix(ui): redirect agent detail page to new URL key after rename Agent detail URLs use a name-derived key, so renaming an agent (or rolling back a config revision that restores an old name) invalidated the reference in the URL. The save handler then refetched the detail query for the stale reference, which 404s and rendered "Agent not found". On save/rollback success, compare the previous and updated route refs; when the key changed, drop the stale cached detail queries and replace the URL with the new canonical reference instead of refetching the old one. Co-Authored-By: Paperclip --- ui/src/pages/AgentDetail.tsx | 43 +++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx index 0a8720d52d9..84ca05df491 100644 --- a/ui/src/pages/AgentDetail.tsx +++ b/ui/src/pages/AgentDetail.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo, useState, useRef } from "react"; -import { useParams, useNavigate, Link, Navigate, useBeforeUnload } from "@/lib/router"; -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { useParams, useNavigate, Link, Navigate, useBeforeUnload, type NavigateFunction } from "@/lib/router"; +import { useQuery, useMutation, useQueryClient, type QueryClient } from "@tanstack/react-query"; import { agentsApi, type AgentKey, @@ -1638,6 +1638,29 @@ function CostsSection({ /* ---- Agent Configure Page ---- */ +/** + * Agent detail URLs use a name-derived key, so updates that change the agent's + * name (a rename or a config-revision rollback) can invalidate the reference + * currently in the URL. When that happens, refetching the old reference would + * 404 with "Agent not found". Instead, drop the stale cached queries and + * replace the URL with the new canonical reference. Returns true when a + * redirect happened. + */ +function syncAgentRouteAfterRename( + queryClient: QueryClient, + navigate: NavigateFunction, + previous: { id: string; urlKey?: string | null; name?: string | null }, + updated: { id: string; urlKey?: string | null; name?: string | null }, + tab: string, +): boolean { + const previousRef = agentRouteRef(previous); + const nextRef = agentRouteRef(updated); + if (nextRef === previousRef) return false; + queryClient.removeQueries({ queryKey: queryKeys.agents.detail(previousRef) }); + navigate(`/agents/${nextRef}/${tab}`, { replace: true }); + return true; +} + function AgentConfigurePage({ agent, agentId, @@ -1658,6 +1681,8 @@ function AgentConfigurePage({ updatePermissions: { mutate: (permissions: AgentPermissionUpdate) => void; isPending: boolean }; }) { const queryClient = useQueryClient(); + const navigate = useNavigate(); + const { tab: urlTab } = useParams<{ tab?: string }>(); const [revisionsOpen, setRevisionsOpen] = useState(false); const { data: configRevisions } = useQuery({ @@ -1667,10 +1692,12 @@ function AgentConfigurePage({ const rollbackConfig = useMutation({ mutationFn: (revisionId: string) => agentsApi.rollbackConfigRevision(agent.id, revisionId, companyId), - onSuccess: () => { + onSuccess: (updated) => { queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.id) }); - queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) }); queryClient.invalidateQueries({ queryKey: queryKeys.agents.configRevisions(agent.id) }); + if (!syncAgentRouteAfterRename(queryClient, navigate, agent, updated, urlTab ?? "configuration")) { + queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) }); + } }, }); @@ -1770,6 +1797,8 @@ function ConfigurationTab({ hideInstructionsFile?: boolean; }) { const queryClient = useQueryClient(); + const navigate = useNavigate(); + const { tab: urlTab } = useParams<{ tab?: string }>(); const { pushToast } = useToastActions(); const [awaitingRefreshAfterSave, setAwaitingRefreshAfterSave] = useState(false); const lastAgentRef = useRef(agent); @@ -1804,11 +1833,13 @@ function ConfigurationTab({ onMutate: () => { setAwaitingRefreshAfterSave(true); }, - onSuccess: () => { + onSuccess: (updated) => { queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.id) }); - queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) }); queryClient.invalidateQueries({ queryKey: queryKeys.agents.configRevisions(agent.id) }); queryClient.invalidateQueries({ queryKey: queryKeys.agents.list(agent.companyId) }); + if (!syncAgentRouteAfterRename(queryClient, navigate, agent, updated, urlTab ?? "configuration")) { + queryClient.invalidateQueries({ queryKey: queryKeys.agents.detail(agent.urlKey) }); + } pushToast({ title: "Agent saved", tone: "success" }); }, onError: (err) => {