diff --git a/desktop/src-tauri/src/managed_agents/personas.rs b/desktop/src-tauri/src/managed_agents/personas.rs index 95f58878b..31762856d 100644 --- a/desktop/src-tauri/src/managed_agents/personas.rs +++ b/desktop/src-tauri/src/managed_agents/personas.rs @@ -15,10 +15,21 @@ struct BuiltInPersona { name_pool: &'static [&'static str], model: Option<&'static str>, runtime: Option<&'static str>, + default_active: bool, } const FIZZ_AVATAR: &str = "data:image/png;base64,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"; +const ANGELICA_AVATAR: &str = + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT1MGeCxaWcvoonCISYyuhWfjngmSE8cyJhfQ&s"; +const BART_AVATAR: &str = + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcShI3uwQ-mSgRdv5WYBibPyo2I3X7ybsYmrVQ&s"; +const CHUCKY_AVATAR: &str = + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ_cxY4ocdqIGx5JVRuZjlCvyhlhEWV5MXWDw&s"; +const MARGE_AVATAR: &str = "https://static0.srcdn.com/wordpress/wp-content/uploads/2023/05/the-simpsons-season-34-marge-blue-hair.jpg"; +const NED_AVATAR: &str = "https://i1.sndcdn.com/artworks-000362506068-4i6lyp-t500x500.jpg"; +const TOMMY_AVATAR: &str = "https://i.redd.it/the-slight-redesigns-of-tommy-pickles-back-in-the-90s-v0-ii3f8rsmv1cf1.jpg?width=1290&format=pjpg&auto=webp&s=e9901c8661d5b3a00e49704d12d02fef87c5947e"; + const FIZZ_SYSTEM_PROMPT: &str = r#"You are Fizz. You are a careful, direct engineering agent with a subtle bee theme: collaborative, industrious, and precise. Keep the bee motif light — no catchphrases, no cartoon impersonation, and no performative roleplay. Reliability beats performance theater. # Subagents and Peers @@ -39,19 +50,142 @@ Don't use subagents when the briefing overhead exceeds the parallelism payoff or Your name is Fizz. You are friendly, helpful, and quietly industrious — more honeycomb than hornet."#; -const BUILT_IN_PERSONAS: &[BuiltInPersona] = &[BuiltInPersona { - id: "builtin:fizz", - display_name: "Fizz", - avatar_url: Some(FIZZ_AVATAR), - system_prompt: FIZZ_SYSTEM_PROMPT, - name_pool: &[ - "Nectar", "Comet", "Bramble", "Clover", "Pollen", "Amber", "Daisy", "Mason", "Bumble", - "Thistle", "Honey", "Waxwing", "Hive", "Meadow", "Juniper", "Aster", "Sage", "Willow", - "Orchard", "Buzz", - ], - model: None, - runtime: None, -}]; +const ANGELICA_SYSTEM_PROMPT: &str = r#"You are Angelica. You help turn broad ideas into clear product and design direction. + +# Focus + +Clarify the goal, identify the audience, and call out the tradeoffs that matter. Keep recommendations concrete and avoid over-polishing before the shape of the work is understood. + +# Collaboration + +Ask sharp questions when the brief is ambiguous. Once the direction is clear, summarize the intended experience and the next useful decision."#; + +const BART_SYSTEM_PROMPT: &str = r#"You are Bart. You are a fast-moving implementation partner who helps turn scoped plans into working changes. + +# Focus + +Prefer small, direct edits that follow the existing codebase. Keep momentum, validate behavior, and flag any risky assumption before it becomes expensive. + +# Collaboration + +Give concise progress updates and keep the work easy for another engineer to review."#; + +const CHUCKY_SYSTEM_PROMPT: &str = r#"You are Chucky. You are a careful QA and edge-case agent who looks for the ways a change might break. + +# Focus + +Inspect state transitions, empty states, permissions, accessibility, and failure paths. Prioritize issues by impact and make the evidence easy to verify. + +# Collaboration + +Be direct but constructive. When possible, suggest the smallest fix that would reduce the risk."#; + +const MARGE_SYSTEM_PROMPT: &str = r#"You are Marge. You are a calm coordination agent who keeps multi-step work organized and grounded. + +# Focus + +Track goals, dependencies, and follow-ups. Keep plans practical, name what is done, and make unresolved work visible without adding ceremony. + +# Collaboration + +Help the team stay aligned. Summarize decisions clearly and keep the next action obvious."#; + +const NED_SYSTEM_PROMPT: &str = r#"You are Ned. You are a friendly support and onboarding agent who helps people feel oriented quickly. + +# Focus + +Explain unfamiliar flows plainly, surface the next useful step, and keep guidance reassuring without becoming verbose. + +# Collaboration + +Use a warm tone, confirm assumptions when needed, and leave people with a clear path forward."#; + +const TOMMY_SYSTEM_PROMPT: &str = r#"You are Tommy. You are an exploratory product-thinking agent who helps turn uncertainty into experiments. + +# Focus + +Break big questions into small trials, name what would prove or disprove an idea, and keep momentum through ambiguity. + +# Collaboration + +Stay curious, practical, and optimistic. Make the next experiment easy to try."#; + +const BUILT_IN_PERSONAS: &[BuiltInPersona] = &[ + BuiltInPersona { + id: "builtin:fizz", + display_name: "Fizz", + avatar_url: Some(FIZZ_AVATAR), + system_prompt: FIZZ_SYSTEM_PROMPT, + name_pool: &[ + "Nectar", "Comet", "Bramble", "Clover", "Pollen", "Amber", "Daisy", "Mason", "Bumble", + "Thistle", "Honey", "Waxwing", "Hive", "Meadow", "Juniper", "Aster", "Sage", "Willow", + "Orchard", "Buzz", + ], + model: None, + runtime: None, + default_active: true, + }, + BuiltInPersona { + id: "builtin:angelica", + display_name: "Angelica", + avatar_url: Some(ANGELICA_AVATAR), + system_prompt: ANGELICA_SYSTEM_PROMPT, + name_pool: &["Angelica"], + model: None, + runtime: None, + default_active: false, + }, + BuiltInPersona { + id: "builtin:bart", + display_name: "Bart", + avatar_url: Some(BART_AVATAR), + system_prompt: BART_SYSTEM_PROMPT, + name_pool: &["Bart"], + model: None, + runtime: None, + default_active: false, + }, + BuiltInPersona { + id: "builtin:chucky", + display_name: "Chucky", + avatar_url: Some(CHUCKY_AVATAR), + system_prompt: CHUCKY_SYSTEM_PROMPT, + name_pool: &["Chucky"], + model: None, + runtime: None, + default_active: false, + }, + BuiltInPersona { + id: "builtin:marge", + display_name: "Marge", + avatar_url: Some(MARGE_AVATAR), + system_prompt: MARGE_SYSTEM_PROMPT, + name_pool: &["Marge"], + model: None, + runtime: None, + default_active: false, + }, + BuiltInPersona { + id: "builtin:ned", + display_name: "Ned", + avatar_url: Some(NED_AVATAR), + system_prompt: NED_SYSTEM_PROMPT, + name_pool: &["Ned"], + model: None, + runtime: None, + default_active: false, + }, + BuiltInPersona { + id: "builtin:tommy", + display_name: "Tommy", + avatar_url: Some(TOMMY_AVATAR), + system_prompt: TOMMY_SYSTEM_PROMPT, + name_pool: &["Tommy"], + model: None, + runtime: None, + default_active: false, + }, +]; const RETIRED_PERSONAS: &[(&str, &str)] = &[ ( @@ -109,7 +243,7 @@ fn built_in_persona_records(now: &str) -> Vec { provider: None, name_pool: persona.name_pool.iter().map(|s| s.to_string()).collect(), is_builtin: true, - is_active: true, + is_active: persona.default_active, source_team: None, source_team_persona_slug: None, env_vars: std::collections::BTreeMap::new(), @@ -254,7 +388,7 @@ pub fn ensure_persona_is_active( if !persona.is_active { return Err(format!( - "{} is not in My Agents. Choose it from Persona Catalog first.", + "{} is not in My Agents. Choose it from Agent Catalog first.", persona.display_name )); } diff --git a/desktop/src-tauri/src/managed_agents/personas/tests.rs b/desktop/src-tauri/src/managed_agents/personas/tests.rs index 5d5d5e6b4..e02b55cc2 100644 --- a/desktop/src-tauri/src/managed_agents/personas/tests.rs +++ b/desktop/src-tauri/src/managed_agents/personas/tests.rs @@ -33,12 +33,20 @@ fn merge_personas_adds_missing_built_ins() { assert!(changed); assert_eq!(records.len(), BUILT_IN_PERSONAS.len()); assert!(records.iter().all(|record| record.is_builtin)); - assert!(records.iter().all(|record| record.is_active)); let display_names: Vec<&str> = records .iter() .map(|record| record.display_name.as_str()) .collect(); - assert_eq!(display_names, vec!["Fizz"]); + assert_eq!( + display_names, + vec!["Fizz", "Angelica", "Bart", "Chucky", "Marge", "Ned", "Tommy"] + ); + let active_ids: Vec<&str> = records + .iter() + .filter(|record| record.is_active) + .map(|record| record.id.as_str()) + .collect(); + assert_eq!(active_ids, vec!["builtin:fizz"]); } #[test] @@ -201,7 +209,7 @@ fn ensure_persona_is_active_rejects_inactive_personas() { assert_eq!( err, - "Fizz is not in My Agents. Choose it from Persona Catalog first." + "Fizz is not in My Agents. Choose it from Agent Catalog first." ); } diff --git a/desktop/src/features/agents/lib/catalog.test.mjs b/desktop/src/features/agents/lib/catalog.test.mjs index 123975776..93ec86b60 100644 --- a/desktop/src/features/agents/lib/catalog.test.mjs +++ b/desktop/src/features/agents/lib/catalog.test.mjs @@ -4,6 +4,7 @@ import test from "node:test"; import { getCatalogPersonas, getCatalogSelectionState, + getLibraryPersonas, getPersonaLabelsById, getPersonaLibraryState, isCatalogPersonaSelected, @@ -80,7 +81,7 @@ test("getCatalogPersonas keeps chooser order stable when selection changes", () ); }); -test("isCatalogPersonaSelected only treats active built-ins as selected", () => { +test("isCatalogPersonaSelected treats active catalog personas as selected", () => { assert.equal( isCatalogPersonaSelected( createPersona("builtin:fizz", "Fizz", { @@ -101,7 +102,7 @@ test("isCatalogPersonaSelected only treats active built-ins as selected", () => ); assert.equal( isCatalogPersonaSelected(createPersona("custom:builder", "Builder")), - false, + true, ); }); @@ -135,3 +136,21 @@ test("getPersonaLibraryState keeps the working library and full catalog in one p ); assert.equal(state.personaLabelsById["builtin:fizz"], "Fizz"); }); + +test("getLibraryPersonas keeps active custom personas even when catalog entries are similar", () => { + const avatarUrl = "https://example.test/marge.png"; + const personas = [ + createPersona("builtin:marge", "Marge", { + avatarUrl, + isBuiltIn: true, + isActive: false, + }), + createPersona("custom:marge", "Marge", { avatarUrl, isActive: true }), + createPersona("custom:builder", "Builder", { isActive: true }), + ]; + + assert.deepEqual( + getLibraryPersonas(personas).map((persona) => persona.id), + ["custom:marge", "custom:builder"], + ); +}); diff --git a/desktop/src/features/agents/lib/catalog.ts b/desktop/src/features/agents/lib/catalog.ts index acc23df96..226aafca5 100644 --- a/desktop/src/features/agents/lib/catalog.ts +++ b/desktop/src/features/agents/lib/catalog.ts @@ -20,20 +20,37 @@ export function getActivePersonas(personas: readonly AgentPersona[]) { return personas.filter(isPersonaActive); } -export function getCatalogPersonas(personas: readonly AgentPersona[]) { +export function getLibraryPersonas(personas: readonly AgentPersona[]) { + return getActivePersonas(personas); +} + +export function isPersonaVisibleInCatalog( + persona: AgentPersona, + sharedCatalogPersonaIds: ReadonlySet = new Set(), +) { + return persona.isBuiltIn || sharedCatalogPersonaIds.has(persona.id); +} + +export function getCatalogPersonas( + personas: readonly AgentPersona[], + sharedCatalogPersonaIds: ReadonlySet = new Set(), +) { return personas - .filter((persona) => persona.isBuiltIn) + .filter((persona) => + isPersonaVisibleInCatalog(persona, sharedCatalogPersonaIds), + ) .sort((left, right) => left.displayName.localeCompare(right.displayName)); } export function isCatalogPersonaSelected(persona: AgentPersona) { - return persona.isBuiltIn && persona.isActive; + return persona.isActive; } export function getCatalogSelectionState( personas: readonly AgentPersona[], + sharedCatalogPersonaIds: ReadonlySet = new Set(), ): CatalogSelectionState { - const catalogPersonas = getCatalogPersonas(personas); + const catalogPersonas = getCatalogPersonas(personas, sharedCatalogPersonaIds); return { catalogPersonas, @@ -52,9 +69,13 @@ export function getPersonaLabelsById(personas: readonly AgentPersona[]) { export function getPersonaLibraryState( personas: readonly AgentPersona[], + sharedCatalogPersonaIds: ReadonlySet = new Set(), ): PersonaLibraryState { - const libraryPersonas = getActivePersonas(personas); - const { catalogPersonas } = getCatalogSelectionState(personas); + const libraryPersonas = getLibraryPersonas(personas); + const { catalogPersonas } = getCatalogSelectionState( + personas, + sharedCatalogPersonaIds, + ); return { catalogPersonas, diff --git a/desktop/src/features/agents/ui/AgentsView.tsx b/desktop/src/features/agents/ui/AgentsView.tsx index 7305a9987..c98c87f3f 100644 --- a/desktop/src/features/agents/ui/AgentsView.tsx +++ b/desktop/src/features/agents/ui/AgentsView.tsx @@ -11,6 +11,7 @@ import { PersonaCatalogDialog } from "./PersonaCatalogDialog"; import { PersonaDialog } from "./PersonaDialog"; import { PersonaDeleteDialog } from "./PersonaDeleteDialog"; import { PersonaImportUpdateDialog } from "./PersonaImportUpdateDialog"; +import { PersonaShareDialog } from "./PersonaShareDialog"; import { RelayDirectorySection } from "./RelayDirectorySection"; import { SecretRevealDialog } from "./SecretRevealDialog"; import { TeamDeleteDialog } from "./TeamDeleteDialog"; @@ -115,6 +116,13 @@ export function AgentsView() { isPersonasPending={personas.isPending} onCreatePersona={personas.openCreate} onChooseCatalog={personas.openCatalog} + onDuplicatePersona={personas.openDuplicate} + onEditPersona={personas.openEdit} + onSharePersona={personas.openShare} + onDeactivatePersona={(persona) => { + void personas.handleSetActive(persona, false, "library"); + }} + onDeletePersona={personas.openDelete} onImportPersonaFile={(fileBytes, fileName) => { void personas.handleImportFile(fileBytes, fileName); }} @@ -240,6 +248,35 @@ export function AgentsView() { persona={personas.personaToDelete} /> ) : null} + {personas.personaToShare ? ( + { + if (personas.personaToShare) { + personas.setPersonaCatalogVisibility( + personas.personaToShare, + visible, + ); + } + }} + onExport={() => { + if (personas.personaToShare) { + personas.handleExport(personas.personaToShare); + } + }} + onOpenChange={(open) => { + if (!open) { + personas.setPersonaToShare(null); + } + }} + open={personas.personaToShare !== null} + persona={personas.personaToShare} + /> + ) : null} {personas.isCatalogDialogOpen ? ( void; onEdit: (persona: AgentPersona) => void; - onExport: (persona: AgentPersona) => void; + onShare: (persona: AgentPersona) => void; onDeactivate: (persona: AgentPersona) => void; onDelete: (persona: AgentPersona) => void; }) { const disabled = isActionPending || isPending; + const canEdit = !persona.isBuiltIn && !persona.sourceTeam; return ( @@ -45,7 +46,11 @@ export function PersonaActionsMenu({ align="end" onCloseAutoFocus={(event) => event.preventDefault()} > - {!persona.isBuiltIn ? ( + onShare(persona)}> + + Share + + {canEdit ? ( onEdit(persona)}> Edit @@ -58,21 +63,8 @@ export function PersonaActionsMenu({ Duplicate - onExport(persona)}> - - Export - - {persona.isBuiltIn ? ( - onDeactivate(persona)} - > - - Remove from My Agents - - ) : persona.sourceTeam ? ( + {persona.sourceTeam ? ( Managed by team @@ -81,10 +73,17 @@ export function PersonaActionsMenu({ onDelete(persona)} + onClick={() => { + if (persona.isBuiltIn) { + onDeactivate(persona); + return; + } + + onDelete(persona); + }} > - Delete + Remove from My Agents )} diff --git a/desktop/src/features/agents/ui/PersonaAddedBy.tsx b/desktop/src/features/agents/ui/PersonaAddedBy.tsx new file mode 100644 index 000000000..66e5ee31f --- /dev/null +++ b/desktop/src/features/agents/ui/PersonaAddedBy.tsx @@ -0,0 +1,14 @@ +import { cn } from "@/shared/lib/cn"; + +type PersonaAddedByProps = { + className?: string; +}; + +export function PersonaAddedBy({ className }: PersonaAddedByProps) { + return ( +

+ Added by{" "} + You +

+ ); +} diff --git a/desktop/src/features/agents/ui/PersonaCatalogDialog.tsx b/desktop/src/features/agents/ui/PersonaCatalogDialog.tsx index bf971f284..8ccc9592c 100644 --- a/desktop/src/features/agents/ui/PersonaCatalogDialog.tsx +++ b/desktop/src/features/agents/ui/PersonaCatalogDialog.tsx @@ -1,11 +1,17 @@ import * as React from "react"; +import { isCatalogPersonaSelected } from "@/features/agents/lib/catalog"; +import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; import type { AgentPersona } from "@/shared/api/types"; +import { useFeedbackToasts } from "@/shared/hooks/useToastEffect"; +import { cn } from "@/shared/lib/cn"; import { Button } from "@/shared/ui/button"; import { Dialog } from "@/shared/ui/dialog"; import { ChooserDialogContent } from "@/shared/ui/chooser-dialog-content"; +import { Markdown } from "@/shared/ui/markdown"; +import { Skeleton } from "@/shared/ui/skeleton"; -import { PersonaCatalogSurface } from "./PersonaCatalogSurface"; +import { PersonaAddedBy } from "./PersonaAddedBy"; import { personaCatalogCopy } from "./personaLibraryCopy"; type PersonaCatalogDialogProps = { @@ -21,6 +27,16 @@ type PersonaCatalogDialogProps = { personas: AgentPersona[]; }; +const agentInstructionMarkdownClassName = [ + "mt-3 leading-6 text-muted-foreground [&_blockquote]:!text-muted-foreground [&_code]:!text-muted-foreground [&_li]:text-muted-foreground [&_ol]:text-muted-foreground [&_p]:text-muted-foreground [&_strong]:text-muted-foreground [&_td]:text-muted-foreground [&_ul]:text-muted-foreground", + "[&>h1]:!text-sm [&>h1]:!font-semibold [&>h1]:!leading-6 [&>h1]:!tracking-normal [&>h1]:!text-foreground", + "[&>h2]:!text-sm [&>h2]:!font-semibold [&>h2]:!leading-6 [&>h2]:!tracking-normal [&>h2]:!text-foreground", + "[&>h3]:!text-sm [&>h3]:!font-semibold [&>h3]:!leading-6 [&>h3]:!tracking-normal [&>h3]:!text-foreground", + "[&>h4]:!text-sm [&>h4]:!font-semibold [&>h4]:!leading-6 [&>h4]:!tracking-normal [&>h4]:!text-foreground", + "[&>h5]:!text-sm [&>h5]:!font-semibold [&>h5]:!leading-6 [&>h5]:!tracking-normal [&>h5]:!text-foreground", + "[&>h6]:!text-sm [&>h6]:!font-semibold [&>h6]:!leading-6 [&>h6]:!tracking-normal [&>h6]:!text-foreground", +].join(" "); + export function PersonaCatalogDialog({ error, feedbackErrorMessage, @@ -34,48 +50,324 @@ export function PersonaCatalogDialog({ personas, }: PersonaCatalogDialogProps) { const contentRef = React.useRef(null); + const [selectedPersonaId, setSelectedPersonaId] = React.useState< + string | null + >(null); + const selectedPersona = React.useMemo(() => { + if (personas.length === 0) { + return null; + } + + return ( + personas.find((persona) => persona.id === selectedPersonaId) ?? + personas[0] + ); + }, [personas, selectedPersonaId]); + + React.useEffect(() => { + if (!open) { + return; + } + + if (personas.length === 0) { + setSelectedPersonaId(null); + return; + } + + setSelectedPersonaId((current) => + current && personas.some((persona) => persona.id === current) + ? current + : personas[0].id, + ); + }, [open, personas]); + + useFeedbackToasts(feedbackNoticeMessage, feedbackErrorMessage); + + const selectedPersonaIsActive = selectedPersona + ? isCatalogPersonaSelected(selectedPersona) + : false; + + const handleUseSelectedPersona = () => { + if (!selectedPersona || selectedPersonaIsActive) { + return; + } + + onClearFeedback(); + onSelectPersona(selectedPersona, true); + }; return ( onOpenChange(false)} - size="sm" - type="button" - variant="outline" - > - Done - - } - footerClassName="justify-end gap-2" - footerTestId="persona-catalog-dialog-footer" + headerClassName="bg-sidebar pb-3 text-sidebar-foreground" headerTestId="persona-catalog-dialog-header" onOpenAutoFocus={(event) => { event.preventDefault(); contentRef.current?.focus(); }} ref={contentRef} - scrollAreaTestId="persona-catalog-dialog-scroll-area" + scrollAreaClassName="flex min-h-0 overflow-hidden px-0" + scrollAreaTestId="persona-catalog-dialog-body" tabIndex={-1} title={personaCatalogCopy.dialogTitle} > - ); } + +type PersonaCatalogChooserProps = { + error: Error | null; + isLoading: boolean; + isPending: boolean; + isSelectedPersonaActive: boolean; + onUsePersona: () => void; + onSelectPersona: (personaId: string) => void; + personas: AgentPersona[]; + selectedPersona: AgentPersona | null; + selectedPersonaId: string | null; +}; + +function PersonaCatalogChooser({ + error, + isLoading, + isPending, + isSelectedPersonaActive, + onUsePersona, + onSelectPersona, + personas, + selectedPersona, + selectedPersonaId, +}: PersonaCatalogChooserProps) { + return ( +
+
+
+ {isLoading ? : null} + + {!isLoading && personas.length > 0 ? ( +
+ {personas.map((persona) => { + const isCurrent = persona.id === selectedPersonaId; + + return ( + + ); + })} +
+ ) : null} +
+
+ +
+
+ {isLoading ? : null} + + {!isLoading && selectedPersona ? ( + + ) : null} + + {!isLoading && personas.length === 0 && !error ? ( +
+
+

+ {personaCatalogCopy.emptyCatalogTitle} +

+

+ {personaCatalogCopy.emptyCatalogDescription} +

+
+
+ ) : null} + + {error ? ( +

+ {error.message} +

+ ) : null} +
+ +
+ +
+
+
+ ); +} + +function PersonaCatalogDetail({ persona }: { persona: AgentPersona }) { + return ( +
+
+ +
+

+ {persona.displayName} +

+ +
+
+ + + +
+

+ Agent instruction +

+ +
+
+ ); +} + +function PersonaCatalogMetaGroup({ + items, +}: { + items: { label: string; value: string }[]; +}) { + return ( +
+
+ {items.map((item, index) => ( +
0 && + "border-t border-border/60 sm:border-t-0 sm:before:absolute sm:before:bottom-3 sm:before:left-0 sm:before:top-3 sm:before:w-px sm:before:bg-border/70", + )} + key={item.label} + > +

+ {item.label} +

+

+ {item.value} +

+
+ ))} +
+
+ ); +} + +function PersonaCatalogListSkeleton() { + return ( +
+ {["first", "second", "third", "fourth", "fifth"].map((key) => ( +
+ + +
+ ))} +
+ ); +} + +function PersonaCatalogDetailSkeleton() { + return ( +
+
+ + +
+
+ + + +
+ +
+ ); +} diff --git a/desktop/src/features/agents/ui/PersonaShareDialog.tsx b/desktop/src/features/agents/ui/PersonaShareDialog.tsx new file mode 100644 index 000000000..0a180fdea --- /dev/null +++ b/desktop/src/features/agents/ui/PersonaShareDialog.tsx @@ -0,0 +1,106 @@ +import { BookUser, Download, X } from "lucide-react"; + +import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; +import type { AgentPersona } from "@/shared/api/types"; +import { Button } from "@/shared/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/shared/ui/dialog"; +import { Separator } from "@/shared/ui/separator"; +import { Switch } from "@/shared/ui/switch"; + +import { PersonaAddedBy } from "./PersonaAddedBy"; + +type PersonaShareDialogProps = { + isCatalogVisible: boolean; + isPending: boolean; + onCatalogVisibilityChange: (visible: boolean) => void; + onExport: () => void; + onOpenChange: (open: boolean) => void; + open: boolean; + persona: AgentPersona; +}; + +export function PersonaShareDialog({ + isCatalogVisible, + isPending, + onCatalogVisibilityChange, + onExport, + onOpenChange, + open, + persona, +}: PersonaShareDialogProps) { + const switchId = `persona-share-catalog-${persona.id}`; + + return ( + + + +
+ Share agent +
+ + + + Close + +
+
+
+ +
+
+ +
+

+ {persona.displayName} +

+ +
+
+ + + +
+
+ + +
+ +
+
+
+
+ ); +} diff --git a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx index 5fe114a30..6475e376a 100644 --- a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx +++ b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx @@ -33,6 +33,7 @@ import { import { IdentityCardSkeleton } from "@/shared/ui/identity-card-skeleton"; import { AgentIdentityCard } from "./AgentIdentityCard"; import { CreateIdentityCard } from "./CreateIdentityCard"; +import { PersonaActionsMenu } from "./PersonaActionsMenu"; import { buildUnifiedGroups, pickProfileAgent } from "./unifiedAgentGroups"; type UnifiedAgentsSectionProps = { @@ -61,6 +62,11 @@ type UnifiedAgentsSectionProps = { isPersonasPending: boolean; onCreatePersona: () => void; onChooseCatalog: () => void; + onDuplicatePersona: (persona: AgentPersona) => void; + onEditPersona: (persona: AgentPersona) => void; + onSharePersona: (persona: AgentPersona) => void; + onDeactivatePersona: (persona: AgentPersona) => void; + onDeletePersona: (persona: AgentPersona) => void; onImportPersonaFile: (fileBytes: number[], fileName: string) => void; }; @@ -91,6 +97,11 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { isPersonasPending, onCreatePersona, onChooseCatalog, + onDuplicatePersona, + onEditPersona, + onSharePersona, + onDeactivatePersona, + onDeletePersona, onImportPersonaFile, } = props; @@ -172,6 +183,18 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { const profileAgent = pickProfileAgent(group.agents); return ( + } agent={profileAgent} key={group.persona.id} persona={group.persona} @@ -270,6 +293,7 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { } function AgentPersonaCard({ + actions, agent, persona, presenceLoaded, @@ -277,6 +301,7 @@ function AgentPersonaCard({ onOpenAgentProfile, onOpenPersonaProfile, }: { + actions?: React.ReactNode; agent: ManagedAgent | undefined; persona: AgentPersona; presenceLoaded: boolean; @@ -296,6 +321,7 @@ function AgentPersonaCard({ return ( typeof id === "string"); + } catch { + return []; + } +} + +function writeSharedCatalogPersonaIds(ids: string[]) { + if (typeof window === "undefined") { + return; + } + + try { + window.localStorage.setItem( + PERSONA_CATALOG_VISIBILITY_STORAGE_KEY, + JSON.stringify(ids), + ); + } catch { + // Catalog visibility is a local convenience setting; ignore storage failures. + } +} + export function usePersonaActions() { const queryClient = useQueryClient(); const personasQuery = usePersonasQuery(); @@ -51,7 +93,12 @@ export function usePersonaActions() { React.useState(null); const [personaToDelete, setPersonaToDelete] = React.useState(null); + const [personaToShare, setPersonaToShare] = + React.useState(null); const [isCatalogDialogOpen, setIsCatalogDialogOpen] = React.useState(false); + const [sharedCatalogPersonaIds, setSharedCatalogPersonaIds] = React.useState< + string[] + >(readSharedCatalogPersonaIds); const [batchImportResult, setBatchImportResult] = React.useState(null); const [batchImportFileName, setBatchImportFileName] = React.useState(""); @@ -65,9 +112,13 @@ export function usePersonaActions() { React.useState("library"); const personas = personasQuery.data ?? []; + const sharedCatalogPersonaIdSet = React.useMemo( + () => new Set(sharedCatalogPersonaIds), + [sharedCatalogPersonaIds], + ); const { catalogPersonas, libraryPersonas, personaLabelsById } = React.useMemo( - () => getPersonaLibraryState(personas), - [personas], + () => getPersonaLibraryState(personas, sharedCatalogPersonaIdSet), + [personas, sharedCatalogPersonaIdSet], ); const personaImportActions = usePersonaImportActions(personas, { @@ -216,6 +267,34 @@ export function usePersonaActions() { setPersonaToDelete(persona); } + function openShare(persona: AgentPersona) { + clearFeedback("library"); + setPersonaToShare(persona); + } + + function setPersonaCatalogVisibility( + persona: AgentPersona, + visible: boolean, + ) { + if (persona.isBuiltIn) { + return; + } + + clearFeedback("library"); + setSharedCatalogPersonaIds((current) => { + const next = new Set(current); + if (visible) { + next.add(persona.id); + } else { + next.delete(persona.id); + } + + const ids = Array.from(next); + writeSharedCatalogPersonaIds(ids); + return ids; + }); + } + const isPending = createPersonaMutation.isPending || updatePersonaMutation.isPending || @@ -237,6 +316,8 @@ export function usePersonaActions() { setPersonaDialogState, personaToDelete, setPersonaToDelete, + personaToShare, + setPersonaToShare, isCatalogDialogOpen, setIsCatalogDialogOpen, batchImportResult, @@ -257,6 +338,9 @@ export function usePersonaActions() { openDuplicate, openCatalog, openDelete, + openShare, + setPersonaCatalogVisibility, + sharedCatalogPersonaIdSet, clearFeedback, }; } diff --git a/desktop/src/testing/e2eBridge.ts b/desktop/src/testing/e2eBridge.ts index 8c9d82558..a430d8ad0 100644 --- a/desktop/src/testing/e2eBridge.ts +++ b/desktop/src/testing/e2eBridge.ts @@ -58,6 +58,15 @@ type MockRelayAgentSeed = { status?: PresenceStatus; }; +type MockPersonaSeed = { + id?: string; + displayName: string; + avatarUrl?: string | null; + systemPrompt: string; + isActive?: boolean; + envVars?: Record; +}; + type MockSearchProfileSeed = { pubkey: string; displayName: string | null; @@ -79,6 +88,7 @@ type E2eConfig = { mcp?: MockCommandAvailability; }; managedAgents?: MockManagedAgentSeed[]; + personas?: MockPersonaSeed[]; relayAgents?: MockRelayAgentSeed[]; agentMemory?: RawAgentMemoryListing | Record; createManagedAgentDelayMs?: number; @@ -1103,21 +1113,96 @@ function resetMockManagedAgents(config?: E2eConfig) { syncMockRelayAgentsFromManagedAgents(); } +const BUILT_IN_PERSONA_AVATAR_URLS = { + angelica: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT1MGeCxaWcvoonCISYyuhWfjngmSE8cyJhfQ&s", + bart: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcShI3uwQ-mSgRdv5WYBibPyo2I3X7ybsYmrVQ&s", + chucky: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ_cxY4ocdqIGx5JVRuZjlCvyhlhEWV5MXWDw&s", + marge: + "https://static0.srcdn.com/wordpress/wp-content/uploads/2023/05/the-simpsons-season-34-marge-blue-hair.jpg", + ned: "https://i1.sndcdn.com/artworks-000362506068-4i6lyp-t500x500.jpg", + tommy: + "https://i.redd.it/the-slight-redesigns-of-tommy-pickles-back-in-the-90s-v0-ii3f8rsmv1cf1.jpg?width=1290&format=pjpg&auto=webp&s=e9901c8661d5b3a00e49704d12d02fef87c5947e", +} as const; + function resetMockPersonas(config?: E2eConfig) { const now = new Date().toISOString(); const activePersonaIds = new Set(config?.mock?.activePersonaIds ?? []); - mockPersonas = [ + const builtInPersonas = [ + { + id: "builtin:angelica", + display_name: "Angelica", + avatar_url: BUILT_IN_PERSONA_AVATAR_URLS.angelica, + system_prompt: + "You are Angelica. You help turn broad ideas into clear product and design direction.\n\n# Focus\n\nClarify the goal, identify the audience, and call out the tradeoffs that matter.", + }, + { + id: "builtin:bart", + display_name: "Bart", + avatar_url: BUILT_IN_PERSONA_AVATAR_URLS.bart, + system_prompt: + "You are Bart. You are a fast-moving implementation partner who helps turn scoped plans into working changes.\n\n# Focus\n\nPrefer small, direct edits that follow the existing codebase.", + }, + { + id: "builtin:chucky", + display_name: "Chucky", + avatar_url: BUILT_IN_PERSONA_AVATAR_URLS.chucky, + system_prompt: + "You are Chucky. You are a careful QA and edge-case agent who looks for the ways a change might break.\n\n# Focus\n\nInspect state transitions, empty states, permissions, accessibility, and failure paths.", + }, { id: "builtin:fizz", display_name: "Fizz", avatar_url: null, system_prompt: "You are Fizz.", - is_builtin: true, - is_active: activePersonaIds.has("builtin:fizz"), - created_at: now, - updated_at: now, + }, + { + id: "builtin:marge", + display_name: "Marge", + avatar_url: BUILT_IN_PERSONA_AVATAR_URLS.marge, + system_prompt: + "You are Marge. You are a calm coordination agent who keeps multi-step work organized and grounded.\n\n# Focus\n\nTrack goals, dependencies, and follow-ups.", + }, + { + id: "builtin:ned", + display_name: "Ned", + avatar_url: BUILT_IN_PERSONA_AVATAR_URLS.ned, + system_prompt: + "You are Ned. You are a friendly support and onboarding agent who helps people feel oriented quickly.\n\n# Focus\n\nExplain unfamiliar flows plainly, surface the next useful step, and keep guidance reassuring without becoming verbose.", + }, + { + id: "builtin:tommy", + display_name: "Tommy", + avatar_url: BUILT_IN_PERSONA_AVATAR_URLS.tommy, + system_prompt: + "You are Tommy. You are an exploratory product-thinking agent who helps turn uncertainty into experiments.\n\n# Focus\n\nBreak big questions into small trials, name what would prove or disprove an idea, and keep momentum through ambiguity.", }, ]; + mockPersonas = builtInPersonas.map((persona) => ({ + id: persona.id, + display_name: persona.display_name, + avatar_url: persona.avatar_url, + system_prompt: persona.system_prompt, + is_builtin: true, + is_active: activePersonaIds.has(persona.id), + created_at: now, + updated_at: now, + })); + + for (const persona of config?.mock?.personas ?? []) { + mockPersonas.push({ + id: persona.id ?? crypto.randomUUID(), + display_name: persona.displayName, + avatar_url: persona.avatarUrl ?? null, + system_prompt: persona.systemPrompt, + is_builtin: false, + is_active: persona.isActive ?? true, + env_vars: { ...(persona.envVars ?? {}) }, + created_at: now, + updated_at: now, + }); + } } function resetMockTeams() { @@ -4923,7 +5008,7 @@ function ensureMockPersonaIsActive(personaId: string) { } if (!persona.is_active) { throw new Error( - `${persona.display_name} is not in My Agents. Choose it from Persona Catalog first.`, + `${persona.display_name} is not in My Agents. Choose it from Agent Catalog first.`, ); } } diff --git a/desktop/tests/e2e/agents.spec.ts b/desktop/tests/e2e/agents.spec.ts index f55c573b7..98548229f 100644 --- a/desktop/tests/e2e/agents.spec.ts +++ b/desktop/tests/e2e/agents.spec.ts @@ -36,12 +36,28 @@ async function openPersonaCatalog(page: import("@playwright/test").Page) { async function getCatalogOrder(page: import("@playwright/test").Page) { return page - .locator('[data-testid^="persona-catalog-card-target-"]') + .locator('[data-testid^="persona-catalog-list-item-"]') .evaluateAll((elements) => elements.map((element) => element.getAttribute("data-testid") ?? ""), ); } +async function selectCatalogPersona( + page: import("@playwright/test").Page, + personaId: string, +) { + await page.getByTestId(`persona-catalog-list-item-${personaId}`).click(); +} + +async function useCatalogPersona( + page: import("@playwright/test").Page, + personaId: string, +) { + await page + .getByTestId(`persona-catalog-use-agent-target-${personaId}`) + .click(); +} + async function waitForInvokeBridge(page: import("@playwright/test").Page) { await page.waitForFunction( () => { @@ -137,9 +153,7 @@ async function invokeTauriExpectError( ); } -test("built-in personas are chosen from the dialog and can be selected", async ({ - page, -}) => { +test("built-in personas are used from the catalog dialog", async ({ page }) => { await page.setViewportSize({ width: 1280, height: 420 }); await gotoApp(page); await page.getByTestId("open-agents-view").click(); @@ -149,6 +163,43 @@ test("built-in personas are chosen from the dialog and can be selected", async ( await expect(page.getByTestId("persona-catalog-dialog")).toContainText( "Fizz", ); + const previewPersonas = [ + ["builtin:angelica", "Angelica"], + ["builtin:bart", "Bart"], + ["builtin:chucky", "Chucky"], + ["builtin:marge", "Marge"], + ["builtin:ned", "Ned"], + ["builtin:tommy", "Tommy"], + ] as const; + const previewPersonaAvatarUrls: Record< + (typeof previewPersonas)[number][0], + string + > = { + "builtin:angelica": + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT1MGeCxaWcvoonCISYyuhWfjngmSE8cyJhfQ&s", + "builtin:bart": + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcShI3uwQ-mSgRdv5WYBibPyo2I3X7ybsYmrVQ&s", + "builtin:chucky": + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ_cxY4ocdqIGx5JVRuZjlCvyhlhEWV5MXWDw&s", + "builtin:marge": + "https://static0.srcdn.com/wordpress/wp-content/uploads/2023/05/the-simpsons-season-34-marge-blue-hair.jpg", + "builtin:ned": + "https://i1.sndcdn.com/artworks-000362506068-4i6lyp-t500x500.jpg", + "builtin:tommy": + "https://i.redd.it/the-slight-redesigns-of-tommy-pickles-back-in-the-90s-v0-ii3f8rsmv1cf1.jpg?width=1290&format=pjpg&auto=webp&s=e9901c8661d5b3a00e49704d12d02fef87c5947e", + }; + for (const [, personaName] of previewPersonas) { + await expect(page.getByTestId("persona-catalog-dialog")).toContainText( + personaName, + ); + } + for (const [personaId, personaName] of previewPersonas) { + await expect( + page + .getByTestId(`persona-catalog-list-item-${personaId}`) + .getByRole("img", { name: `${personaName} avatar` }), + ).toHaveAttribute("src", previewPersonaAvatarUrls[personaId]); + } await expect(page.getByTestId("persona-catalog-dialog-header")).toBeVisible(); await expect( page.getByTestId("persona-catalog-dialog-scroll-area"), @@ -166,11 +217,15 @@ test("built-in personas are chosen from the dialog and can be selected", async ( expect(catalogScrollAreaMetrics.scrollHeight).toBeGreaterThanOrEqual( catalogScrollAreaMetrics.clientHeight, ); - await expect(page.getByTestId("persona-catalog-dialog-footer")).toBeVisible(); + await expect(page.getByTestId("persona-catalog-dialog-body")).toBeVisible(); + await expect(page.getByTestId("persona-catalog-dialog")).not.toContainText( + "Done", + ); await expect(page.getByRole("tooltip")).toHaveCount(0); const initialCatalogOrder = await getCatalogOrder(page); - await page.getByTestId("persona-catalog-card-target-builtin:fizz").click(); + await selectCatalogPersona(page, "builtin:fizz"); + await useCatalogPersona(page, "builtin:fizz"); await expect( page .locator("[data-sonner-toast]") @@ -181,44 +236,41 @@ test("built-in personas are chosen from the dialog and can be selected", async ( "Fizz", ); await expect( - page.getByTestId("persona-catalog-card-target-builtin:fizz"), - ).toHaveAttribute("aria-pressed", "true"); - await expect.poll(() => getCatalogOrder(page)).toEqual(initialCatalogOrder); - - await page.getByTestId("persona-catalog-card-target-builtin:fizz").click(); - await expect( - page - .locator("[data-sonner-toast]") - .filter({ hasText: "Deselected Fizz from My Agents." }), - ).toBeVisible(); + page.getByTestId("persona-catalog-use-agent-target-builtin:fizz"), + ).toHaveText("Added to My Agents"); await expect( - page.getByTestId("persona-catalog-card-target-builtin:fizz"), - ).toHaveAttribute("aria-pressed", "false"); + page.getByTestId("persona-catalog-use-agent-target-builtin:fizz"), + ).toBeDisabled(); + await expect(page.getByTestId("persona-catalog-dialog")).not.toContainText( + "Remove from My Agents", + ); await expect.poll(() => getCatalogOrder(page)).toEqual(initialCatalogOrder); }); -test("persona catalog can reopen from the populated library header", async ({ +test("agent catalog can reopen from the populated library header", async ({ page, }) => { await gotoApp(page); await page.getByTestId("open-agents-view").click(); await openPersonaCatalog(page); - await page.getByTestId("persona-catalog-card-target-builtin:fizz").click(); + await selectCatalogPersona(page, "builtin:fizz"); + await useCatalogPersona(page, "builtin:fizz"); await expect(page.getByTestId("agents-library-personas")).toContainText( "Fizz", ); - await page.getByTestId("persona-catalog-dialog-done").click(); + await page.keyboard.press("Escape"); await openPersonaCatalog(page); await expect(page.getByTestId("persona-catalog-dialog")).toBeVisible(); + await selectCatalogPersona(page, "builtin:fizz"); await expect( - page.getByTestId("persona-catalog-card-target-builtin:fizz"), - ).toHaveAttribute("aria-pressed", "true"); + page.getByTestId("persona-catalog-use-agent-target-builtin:fizz"), + ).toBeDisabled(); }); -test("persona catalog chooser order stays stable when selection changes", async ({ +test("agent catalog chooser order stays stable when selection changes", async ({ page, }) => { await gotoApp(page); @@ -227,7 +279,8 @@ test("persona catalog chooser order stays stable when selection changes", async const before = await getCatalogOrder(page); - await page.getByTestId("persona-catalog-card-target-builtin:fizz").click(); + await selectCatalogPersona(page, "builtin:fizz"); + await useCatalogPersona(page, "builtin:fizz"); await expect( page .locator("[data-sonner-toast]") @@ -237,47 +290,106 @@ test("persona catalog chooser order stays stable when selection changes", async expect(await getCatalogOrder(page)).toEqual(before); }); -test("catalog details sheet shows the full persona details", async ({ - page, -}) => { +test("catalog detail pane shows the full persona details", async ({ page }) => { await gotoApp(page); await page.getByTestId("open-agents-view").click(); await openPersonaCatalog(page); - await page.getByTestId("persona-catalog-details-builtin:fizz").click(); - const detailSelectionTarget = page.getByTestId( - "persona-catalog-detail-selection-target-builtin:fizz", + await selectCatalogPersona(page, "builtin:fizz"); + const useAgentTarget = page.getByTestId( + "persona-catalog-use-agent-target-builtin:fizz", ); - await expect(page.getByTestId("persona-catalog-details-sheet")).toContainText( + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( "Fizz", ); - await expect(page.getByTestId("persona-catalog-details-sheet")).toContainText( + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( + "Added by You", + ); + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( "You are Fizz.", ); - await expect( - page.getByTestId("persona-catalog-detail-selection-title"), - ).toHaveText("Available in Persona Catalog"); - await expect(detailSelectionTarget).toHaveAttribute( - "aria-label", - "Select Fizz in My Agents", + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( + "Built-in persona", ); - await expect(detailSelectionTarget).toHaveAttribute("aria-pressed", "false"); - - await detailSelectionTarget.click(); - await expect( - page.getByTestId("persona-catalog-detail-selection-title"), - ).toHaveText("Selected for My Agents"); - await expect(detailSelectionTarget).toHaveAttribute( + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( + "Preferred model", + ); + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( + "Preferred runtime", + ); + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( + "Agent instruction", + ); + await expect(useAgentTarget).toHaveAttribute( "aria-label", - "Deselect Fizz in My Agents", + "Add Fizz from Agent Catalog", ); - await expect(detailSelectionTarget).toHaveAttribute("aria-pressed", "true"); + await expect(useAgentTarget).toHaveText("Add agent"); + + await useAgentTarget.click(); await expect(page.getByTestId("agents-library-personas")).toContainText( "Fizz", ); }); +test("custom personas can be shared into the agent catalog", async ({ + page, +}) => { + const analystPersonaId = "custom:analyst"; + await installMockBridge(page, { + personas: [ + { + id: analystPersonaId, + displayName: "Analyst", + systemPrompt: "You are Analyst.", + }, + ], + }); + await gotoApp(page); + + await page.getByTestId("open-agents-view").click(); + await expect(page.getByTestId("agents-library-personas")).toContainText( + "Analyst", + ); + + await page.getByLabel("Open actions for Analyst").click(); + await expect(page.getByRole("menuitem")).toHaveText([ + "Share", + "Edit", + "Duplicate", + "Remove from My Agents", + ]); + await expect(page.getByRole("menuitem", { name: "Share" })).toBeVisible(); + await expect(page.getByRole("menuitem", { name: "Export" })).toHaveCount(0); + await page.getByRole("menuitem", { name: "Share" }).click(); + + await expect(page.getByTestId("persona-share-dialog")).toBeVisible(); + await expect(page.getByTestId("persona-share-dialog")).not.toContainText( + "Share options for Analyst.", + ); + await expect(page.getByTestId("persona-share-dialog")).toContainText( + "Added by You", + ); + await expect(page.getByTestId("persona-share-copy-link")).toHaveCount(0); + await expect(page.getByText("Show in agent catalog")).toBeVisible(); + await expect(page.getByTestId("persona-share-export")).toBeVisible(); + await page.getByTestId("persona-share-show-in-catalog").click(); + await page.keyboard.press("Escape"); + + await openPersonaCatalog(page); + await expect( + page.getByTestId(`persona-catalog-list-item-${analystPersonaId}`), + ).toContainText("Analyst"); + await selectCatalogPersona(page, analystPersonaId); + await expect(page.getByTestId("persona-catalog-detail-pane")).toContainText( + "Custom persona", + ); + await expect( + page.getByTestId(`persona-catalog-use-agent-target-${analystPersonaId}`), + ).toHaveText("Added to My Agents"); +}); + test("inactive built-ins cannot be used to create teams", async ({ page }) => { await gotoApp(page); @@ -289,18 +401,17 @@ test("inactive built-ins cannot be used to create teams", async ({ page }) => { }); expect(error).toBe( - "Fizz is not in My Agents. Choose it from Persona Catalog first.", + "Fizz is not in My Agents. Choose it from Agent Catalog first.", ); }); -test("built-in deselection failures show up in Persona Catalog", async ({ - page, -}) => { +test("built-in removal failures show up from My Agents", async ({ page }) => { await gotoApp(page); await page.getByTestId("open-agents-view").click(); await openPersonaCatalog(page); - await page.getByTestId("persona-catalog-card-target-builtin:fizz").click(); + await selectCatalogPersona(page, "builtin:fizz"); + await useCatalogPersona(page, "builtin:fizz"); await invokeTauri(page, "create_team", { input: { @@ -309,7 +420,9 @@ test("built-in deselection failures show up in Persona Catalog", async ({ }, }); - await page.getByTestId("persona-catalog-card-target-builtin:fizz").click(); + await page.keyboard.press("Escape"); + await page.getByLabel("Open actions for Fizz").click(); + await page.getByRole("menuitem", { name: "Remove from My Agents" }).click(); await expect( page diff --git a/desktop/tests/helpers/bridge.ts b/desktop/tests/helpers/bridge.ts index 8d317d7fe..5d98a4074 100644 --- a/desktop/tests/helpers/bridge.ts +++ b/desktop/tests/helpers/bridge.ts @@ -76,6 +76,15 @@ type MockRelayAgentSeed = { status?: "online" | "away" | "offline"; }; +type MockPersonaSeed = { + id?: string; + displayName: string; + avatarUrl?: string | null; + systemPrompt: string; + isActive?: boolean; + envVars?: Record; +}; + export type MockEngramEntry = { slug: string; body: string; @@ -104,6 +113,7 @@ type MockBridgeOptions = { mcp?: MockCommandAvailability; }; managedAgents?: MockManagedAgentSeed[]; + personas?: MockPersonaSeed[]; relayAgents?: MockRelayAgentSeed[]; createManagedAgentDelayMs?: number; channelsReadError?: string;