diff --git a/desktop/src/features/agents/ui/AgentIdentityCard.tsx b/desktop/src/features/agents/ui/AgentIdentityCard.tsx index a66fb9cd7..8dea118cb 100644 --- a/desktop/src/features/agents/ui/AgentIdentityCard.tsx +++ b/desktop/src/features/agents/ui/AgentIdentityCard.tsx @@ -12,6 +12,7 @@ type AgentIdentityCardProps = { label: string; modelLabel: string; onClick: () => void; + status?: ReactNode; }; export function AgentIdentityCard({ @@ -22,6 +23,7 @@ export function AgentIdentityCard({ label, modelLabel, onClick, + status, }: AgentIdentityCardProps) { const trimmedAvatarUrl = avatarUrl?.trim() || null; @@ -56,6 +58,12 @@ export function AgentIdentityCard({
{actions}
) : null} + {status ? ( +
+ {status} +
+ ) : null} +
{label} diff --git a/desktop/src/features/agents/ui/TeamIdentityCard.tsx b/desktop/src/features/agents/ui/TeamIdentityCard.tsx index 17d129e6f..9255f9282 100644 --- a/desktop/src/features/agents/ui/TeamIdentityCard.tsx +++ b/desktop/src/features/agents/ui/TeamIdentityCard.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from "react"; -import { Link, Users } from "lucide-react"; +import { Info, Link, Users } from "lucide-react"; import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar"; import type { AgentPersona } from "@/shared/api/types"; @@ -28,6 +28,7 @@ export function TeamIdentityCard({ actions, children, dataTestId, + description, isSymlink = false, memberCount, personas, @@ -37,6 +38,7 @@ export function TeamIdentityCard({ version, }: TeamIdentityCardProps) { const footerModelLabel = getTeamFooterModelLabel(personas); + const trimmedDescription = description?.trim(); return ( ) : null} + {trimmedDescription ? ( + + + + + +

{trimmedDescription}

+
+
+ ) : null}
{actions}
diff --git a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx index a63018443..93ab5509d 100644 --- a/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx +++ b/desktop/src/features/agents/ui/UnifiedAgentsSection.tsx @@ -15,7 +15,9 @@ import { } from "lucide-react"; import { toast } from "sonner"; +import { useActiveAgentTurns } from "@/features/agents/activeAgentTurnsStore"; import { isManagedAgentActive } from "@/features/agents/lib/managedAgentControlActions"; +import { AgentStatusBadge } from "@/features/agents/ui/AgentStatusBadge"; import { useUserProfileQuery } from "@/features/profile/hooks"; import type { AgentPersona, @@ -24,6 +26,7 @@ import type { } from "@/shared/api/types"; import { useFeedbackToasts } from "@/shared/hooks/useToastEffect"; import { useFileImportZone } from "@/shared/hooks/useFileImportZone"; +import { normalizePubkey } from "@/shared/lib/pubkey"; import { Button } from "@/shared/ui/button"; import { DropdownMenu, @@ -36,6 +39,7 @@ import { IdentityCardSkeleton } from "@/shared/ui/identity-card-skeleton"; import { AgentIdentityCard } from "./AgentIdentityCard"; import { CreateIdentityCard } from "./CreateIdentityCard"; import { EditAgentDialog } from "./EditAgentDialog"; +import { ManagedAgentLogPanel } from "./ManagedAgentLogPanel"; type UnifiedAgentsSectionProps = { actionErrorMessage: string | null; @@ -121,6 +125,12 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { agentsError, isActionPending, isAgentsLoading, + logContent, + logError, + logLoading, + presenceLoaded, + presenceLookup, + selectedLogAgentPubkey, onAddToChannel, onBulkRemoveStopped, onBulkStopRunning, @@ -170,6 +180,14 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { } return additional; }, [groups]); + const selectedLogAgent = React.useMemo( + () => + selectedLogAgentPubkey + ? (agents.find((agent) => agent.pubkey === selectedLogAgentPubkey) ?? + null) + : null, + [agents, selectedLogAgentPubkey], + ); const [collapsed, setCollapsed] = React.useState>(new Set()); const { fileInputRef, @@ -249,6 +267,8 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { key={group.persona.id} persona={group.persona} personaMenuProps={personaMenuProps} + presenceLoaded={presenceLoaded} + presenceLookup={presenceLookup} onOpenAgentProfile={onOpenAgentProfile} /> ); @@ -270,6 +290,8 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { collapsed={collapsed} groupKey="__additional_persona_agents__" label="Additional agent instances" + presenceLoaded={presenceLoaded} + presenceLookup={presenceLookup} onToggle={toggle} onOpenAgentProfile={onOpenAgentProfile} /> @@ -281,6 +303,8 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { collapsed={collapsed} groupKey="__unknown__" label="Unknown Persona" + presenceLoaded={presenceLoaded} + presenceLookup={presenceLookup} onToggle={toggle} onOpenAgentProfile={onOpenAgentProfile} /> @@ -292,10 +316,25 @@ export function UnifiedAgentsSection(props: UnifiedAgentsSectionProps) { collapsed={collapsed} groupKey="__ungrouped__" label="Custom Agents" + presenceLoaded={presenceLoaded} + presenceLookup={presenceLookup} onToggle={toggle} onOpenAgentProfile={onOpenAgentProfile} /> ) : null} + {selectedLogAgent ? ( +
+ +
+ ) : null} ) : null} @@ -371,12 +410,16 @@ function AgentPersonaCard({ agentMenuProps, persona, personaMenuProps, + presenceLoaded, + presenceLookup, onOpenAgentProfile, }: { agent: ManagedAgent | undefined; agentMenuProps: AgentMenuProps; persona: AgentPersona; personaMenuProps: PersonaMenuProps; + presenceLoaded: boolean; + presenceLookup: PresenceLookup; onOpenAgentProfile?: (pubkey: string) => void; }) { const title = persona.displayName; @@ -408,6 +451,15 @@ function AgentPersonaCard({ } personaMenuProps.onEditPersona(persona); }} + status={ + agent ? ( + + ) : null + } /> ); } @@ -415,10 +467,14 @@ function AgentPersonaCard({ function StandaloneAgentCard({ agent, agentMenuProps, + presenceLoaded, + presenceLookup, onOpenAgentProfile, }: { agent: ManagedAgent; agentMenuProps: AgentMenuProps; + presenceLoaded: boolean; + presenceLookup: PresenceLookup; onOpenAgentProfile?: (pubkey: string) => void; }) { const title = agent.name; @@ -439,6 +495,35 @@ function StandaloneAgentCard({ agentMenuProps.onOpenLogs(agent.pubkey); } }} + status={ + + } + /> + ); +} + +function AgentCardStatus({ + agent, + presenceLoaded, + presenceLookup, +}: { + agent: ManagedAgent; + presenceLoaded: boolean; + presenceLookup: PresenceLookup; +}) { + const activeTurns = useActiveAgentTurns(agent.pubkey); + const presenceStatus = presenceLookup[normalizePubkey(agent.pubkey)]; + + return ( + 0} + presenceLoaded={presenceLoaded} + presenceStatus={presenceStatus} + status={agent.status} /> ); } @@ -465,6 +550,9 @@ function AgentPersonaActionsMenu({