From d3d24c86abb9ff58a51fa3cc6f3e475026bb9a46 Mon Sep 17 00:00:00 2001 From: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@sprout-oss.stage.blox.sqprod.co> Date: Fri, 3 Jul 2026 14:04:46 -0700 Subject: [PATCH] feat(desktop): delete saved templates from the New Agent catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Save as template had no inverse: a saved template could never be removed from the catalog. Add a Delete affordance to CreateAgentStartDialog for saved templates — an outline button next to Use template, gated behind a confirm AlertDialog matching TeamDeleteDialog. Deletion drives the previously-unused useDeletePersonaMutation, which now also invalidates agentTemplatesQueryKey so the catalog drops the row immediately; backend validation errors (e.g. record still referenced by a team) surface in the detail pane. Co-authored-by: Taylor Ho Signed-off-by: Taylor Ho --- desktop/src/features/agents/hooks.ts | 3 + .../agents/ui/CreateAgentStartDialog.tsx | 113 +++++++++++++++++- 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/desktop/src/features/agents/hooks.ts b/desktop/src/features/agents/hooks.ts index aa7227345..f3b1bad8e 100644 --- a/desktop/src/features/agents/hooks.ts +++ b/desktop/src/features/agents/hooks.ts @@ -372,6 +372,9 @@ export function useDeletePersonaMutation() { await Promise.all([ queryClient.invalidateQueries({ queryKey: personasQueryKey }), queryClient.invalidateQueries({ queryKey: managedAgentsQueryKey }), + // Saved templates in the New Agent catalog are persona records — + // deleting one must drop it from the catalog immediately. + queryClient.invalidateQueries({ queryKey: agentTemplatesQueryKey }), ]); }, }); diff --git a/desktop/src/features/agents/ui/CreateAgentStartDialog.tsx b/desktop/src/features/agents/ui/CreateAgentStartDialog.tsx index 41cd8a45f..07534dd21 100644 --- a/desktop/src/features/agents/ui/CreateAgentStartDialog.tsx +++ b/desktop/src/features/agents/ui/CreateAgentStartDialog.tsx @@ -1,11 +1,24 @@ -import { FileUp, Plus } from "lucide-react"; +import { FileUp, Plus, Trash2 } from "lucide-react"; import * as React from "react"; -import { useAgentTemplatesQuery } from "@/features/agents/hooks"; +import { + useAgentTemplatesQuery, + useDeletePersonaMutation, +} from "@/features/agents/hooks"; import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; import type { AgentTemplate } from "@/shared/api/types"; import { useFileImportZone } from "@/shared/hooks/useFileImportZone"; import { cn } from "@/shared/lib/cn"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/shared/ui/alert-dialog"; import { Button } from "@/shared/ui/button"; import { ChooserDialogContent } from "@/shared/ui/chooser-dialog-content"; import { Dialog } from "@/shared/ui/dialog"; @@ -66,6 +79,10 @@ export function CreateAgentStartDialog({ const [selectedTemplateId, setSelectedTemplateId] = React.useState< string | null >(null); + const [templateToDelete, setTemplateToDelete] = + React.useState(null); + const [deleteError, setDeleteError] = React.useState(null); + const deletePersonaMutation = useDeletePersonaMutation(); const selectedTemplate = React.useMemo(() => { if (templates.length === 0) { return null; @@ -85,6 +102,8 @@ export function CreateAgentStartDialog({ React.useEffect(() => { if (!open) { + setDeleteError(null); + setTemplateToDelete(null); return; } @@ -108,6 +127,28 @@ export function CreateAgentStartDialog({ onPickTemplate(selectedTemplate); }; + const handleConfirmDelete = async () => { + if (!templateToDelete) { + return; + } + + setDeleteError(null); + try { + // Saved templates are persona records; deleting the record removes + // the template. Backend validation rejects deletion while a team + // still references the record, surfaced via deleteError below. + await deletePersonaMutation.mutateAsync(templateToDelete.id); + // The selection effect reselects templates[0] once the deleted id + // drops out of the refetched list. + setTemplateToDelete(null); + } catch (error) { + setTemplateToDelete(null); + setDeleteError( + error instanceof Error ? error.message : "Failed to delete template.", + ); + } + }; + return ( ) : null} + + {deleteError ? ( +

+ {deleteError} +

+ ) : null} -
+
+ {selectedTemplate?.source === "saved" ? ( + + ) : null} + + + + + + +
); }