diff --git a/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx b/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx
index 99669248..f2549851 100644
--- a/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx
+++ b/frontend/src/app/(dashboard)/dashboard/_components/dashboard-content.tsx
@@ -25,7 +25,7 @@ import { useAccounts } from '@/hooks/use-accounts';
import { config } from '@/lib/config';
import { useInitiateAgentWithInvalidation } from '@/hooks/react-query/dashboard/use-initiate-agent';
import { ModalProviders } from '@/providers/modal-providers';
-import { AgentSelector } from '@/components/dashboard/agent-selector';
+import { useAgents } from '@/hooks/react-query/agents/use-agents';
import { cn } from '@/lib/utils';
import { useModal } from '@/hooks/use-modal-store';
import { Examples } from './suggestions/examples';
@@ -52,6 +52,20 @@ export function DashboardContent() {
const initiateAgentMutation = useInitiateAgentWithInvalidation();
const { onOpen } = useModal();
+ // Fetch agents to get the selected agent's name
+ const { data: agentsResponse } = useAgents({
+ limit: 100,
+ sort_by: 'name',
+ sort_order: 'asc'
+ });
+
+ const agents = agentsResponse?.agents || [];
+ const selectedAgent = selectedAgentId
+ ? agents.find(agent => agent.agent_id === selectedAgentId)
+ : null;
+ const displayName = selectedAgent?.name || 'Suna';
+ const agentAvatar = selectedAgent?.avatar;
+
const threadQuery = useThreadQuery(initiatedThreadId || '');
useEffect(() => {
@@ -189,24 +203,25 @@ export function DashboardContent() {
)}
-
Hey, I am
-
+
+ {displayName}
+ {agentAvatar && (
+
+ {agentAvatar}
+
+ )}
+
What would you like to do today?
-
-
-
(
subscriptionStatus={subscriptionStatus}
canAccessModel={canAccessModel}
refreshCustomModels={refreshCustomModels}
+
+ selectedAgentId={selectedAgentId}
+ onAgentSelect={onAgentSelect}
/>
diff --git a/frontend/src/components/thread/chat-input/chat-settings-dialog.tsx b/frontend/src/components/thread/chat-input/chat-settings-dialog.tsx
new file mode 100644
index 00000000..75628ab1
--- /dev/null
+++ b/frontend/src/components/thread/chat-input/chat-settings-dialog.tsx
@@ -0,0 +1,99 @@
+'use client';
+
+import React, { useState } from 'react';
+import { Settings, X } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from '@/components/ui/dialog';
+import { Label } from '@/components/ui/label';
+import { ModelSelector } from './model-selector';
+import { SubscriptionStatus } from './_use-model-selection';
+import { cn } from '@/lib/utils';
+
+interface ChatSettingsDialogProps {
+ selectedModel: string;
+ onModelChange: (model: string) => void;
+ modelOptions: any[];
+ subscriptionStatus: SubscriptionStatus;
+ canAccessModel: (modelId: string) => boolean;
+ refreshCustomModels?: () => void;
+ disabled?: boolean;
+ className?: string;
+ open?: boolean;
+ onOpenChange?: (open: boolean) => void;
+}
+
+export function ChatSettingsDialog({
+ selectedModel,
+ onModelChange,
+ modelOptions,
+ subscriptionStatus,
+ canAccessModel,
+ refreshCustomModels,
+ disabled = false,
+ className,
+ open: controlledOpen,
+ onOpenChange: controlledOnOpenChange,
+}: ChatSettingsDialogProps) {
+ const [internalOpen, setInternalOpen] = useState(false);
+
+ const open = controlledOpen !== undefined ? controlledOpen : internalOpen;
+ const setOpen = controlledOnOpenChange || setInternalOpen;
+
+ return (
+
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx b/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx
new file mode 100644
index 00000000..a0cfb896
--- /dev/null
+++ b/frontend/src/components/thread/chat-input/chat-settings-dropdown.tsx
@@ -0,0 +1,308 @@
+'use client';
+
+import React, { useState } from 'react';
+import { Settings, ChevronRight, Bot, Presentation, Video, Code, FileSpreadsheet, Search, Plus, Star, User, Sparkles, Database, MessageSquare, Calculator, Palette, Zap } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from '@/components/ui/popover';
+import { Badge } from '@/components/ui/badge';
+import { Separator } from '@/components/ui/separator';
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from '@/components/ui/tooltip';
+import { ChatSettingsDialog } from './chat-settings-dialog';
+import { SubscriptionStatus } from './_use-model-selection';
+import { useAgents } from '@/hooks/react-query/agents/use-agents';
+import { useFeatureFlag } from '@/lib/feature-flags';
+import { useRouter } from 'next/navigation';
+import { cn } from '@/lib/utils';
+import { Label } from '@/components/ui/label';
+
+interface PredefinedAgent {
+ id: string;
+ name: string;
+ description: string;
+ icon: React.ReactNode;
+ category: 'productivity' | 'creative' | 'development';
+}
+
+const PREDEFINED_AGENTS: PredefinedAgent[] = [
+ {
+ id: 'slides',
+ name: 'Slides Pro',
+ description: 'Create stunning presentations and slide decks',
+ icon: ,
+ category: 'productivity'
+ },
+ {
+ id: 'sheets',
+ name: 'Data Analyst',
+ description: 'Spreadsheet and data analysis expert',
+ icon: ,
+ category: 'productivity'
+ }
+];
+
+interface ChatSettingsDropdownProps {
+ selectedAgentId?: string;
+ onAgentSelect?: (agentId: string | undefined) => void;
+ selectedModel: string;
+ onModelChange: (model: string) => void;
+ modelOptions: any[];
+ subscriptionStatus: SubscriptionStatus;
+ canAccessModel: (modelId: string) => boolean;
+ refreshCustomModels?: () => void;
+ disabled?: boolean;
+ className?: string;
+}
+
+export function ChatSettingsDropdown({
+ selectedAgentId,
+ onAgentSelect,
+ selectedModel,
+ onModelChange,
+ modelOptions,
+ subscriptionStatus,
+ canAccessModel,
+ refreshCustomModels,
+ disabled = false,
+ className,
+}: ChatSettingsDropdownProps) {
+ const [dropdownOpen, setDropdownOpen] = useState(false);
+ const [dialogOpen, setDialogOpen] = useState(false);
+ const router = useRouter();
+
+ // Check if custom agents feature is enabled
+ const { enabled: customAgentsEnabled, loading: flagsLoading } = useFeatureFlag('custom_agents');
+
+ // Fetch real agents from API only if feature is enabled
+ const { data: agentsResponse, isLoading: agentsLoading, refetch: loadAgents } = useAgents({
+ limit: 100,
+ sort_by: 'name',
+ sort_order: 'asc'
+ });
+
+ const agents = (customAgentsEnabled && agentsResponse?.agents) || [];
+ const defaultAgent = agents.find(agent => agent.is_default);
+
+ // Find selected agent - could be from real agents or predefined
+ const selectedRealAgent = agents.find(a => a.agent_id === selectedAgentId);
+ const selectedPredefinedAgent = PREDEFINED_AGENTS.find(a => a.id === selectedAgentId);
+ const selectedAgent = selectedRealAgent || selectedPredefinedAgent;
+
+ const handleAgentSelect = (agentId: string | undefined) => {
+ onAgentSelect?.(agentId);
+ setDropdownOpen(false);
+ };
+
+ const handleMoreOptions = () => {
+ setDropdownOpen(false);
+ setDialogOpen(true);
+ };
+
+ const handleExploreAll = () => {
+ setDropdownOpen(false);
+ router.push('/agents');
+ };
+
+ const getAgentDisplay = () => {
+ if (selectedRealAgent) {
+ return {
+ name: selectedRealAgent.name,
+ icon: ,
+ avatar: selectedRealAgent.avatar
+ };
+ }
+ if (selectedPredefinedAgent) {
+ return {
+ name: selectedPredefinedAgent.name,
+ icon: selectedPredefinedAgent.icon,
+ avatar: null
+ };
+ }
+ return {
+ name: 'Suna',
+ icon: ,
+ avatar: null
+ };
+ };
+
+ const agentDisplay = getAgentDisplay();
+
+ const AgentCard = ({ agent, isSelected, onClick, type }: {
+ agent: any;
+ isSelected: boolean;
+ onClick: () => void;
+ type: 'predefined' | 'custom' | 'default';
+ }) => (
+
+
+
+ {type === 'default' ? (
+
+ ) : type === 'custom' ? (
+ agent.avatar
+ ) : (
+ agent.icon
+ )}
+
+
+ {agent.name}
+
+
+
+ );
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ {agentDisplay.name}
+ {agentDisplay.avatar && ` ${agentDisplay.avatar}`}
+
+
+
+
+
+
+
+
+
Choose Your Agent
+
+
+
+
+
+
+
+
handleAgentSelect(undefined)}
+ type="default"
+ />
+
+ {PREDEFINED_AGENTS.map((agent) => (
+
handleAgentSelect(agent.id)}
+ type="predefined"
+ />
+ ))}
+
+
+
+
+
+
+ {agentsLoading ? (
+
+ Loading...
+
+ ) : agents.length > 0 ? (
+
+ {agents.map((agent) => (
+
handleAgentSelect(agent.agent_id)}
+ type="custom"
+ />
+ ))}
+
+ ) : (
+
+
+
No custom agents yet
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/frontend/src/components/thread/chat-input/file-upload-handler.tsx b/frontend/src/components/thread/chat-input/file-upload-handler.tsx
index 4485f231..951a17fc 100644
--- a/frontend/src/components/thread/chat-input/file-upload-handler.tsx
+++ b/frontend/src/components/thread/chat-input/file-upload-handler.tsx
@@ -261,7 +261,6 @@ export const FileUploadHandler = forwardRef<
) : (
)}
- Attachments
diff --git a/frontend/src/components/thread/chat-input/message-input.tsx b/frontend/src/components/thread/chat-input/message-input.tsx
index 97488a89..e356324d 100644
--- a/frontend/src/components/thread/chat-input/message-input.tsx
+++ b/frontend/src/components/thread/chat-input/message-input.tsx
@@ -7,8 +7,10 @@ import { UploadedFile } from './chat-input';
import { FileUploadHandler } from './file-upload-handler';
import { VoiceRecorder } from './voice-recorder';
import { ModelSelector } from './model-selector';
+import { ChatSettingsDropdown } from './chat-settings-dropdown';
import { SubscriptionStatus } from './_use-model-selection';
import { isLocalMode } from '@/lib/config';
+import { useFeatureFlag } from '@/lib/feature-flags';
import { TooltipContent } from '@/components/ui/tooltip';
import { Tooltip } from '@/components/ui/tooltip';
import { TooltipProvider, TooltipTrigger } from '@radix-ui/react-tooltip';
@@ -41,6 +43,8 @@ interface MessageInputProps {
subscriptionStatus: SubscriptionStatus;
canAccessModel: (modelId: string) => boolean;
refreshCustomModels?: () => void;
+ selectedAgentId?: string;
+ onAgentSelect?: (agentId: string | undefined) => void;
}
export const MessageInput = forwardRef(
@@ -73,9 +77,14 @@ export const MessageInput = forwardRef(
subscriptionStatus,
canAccessModel,
refreshCustomModels,
+
+ selectedAgentId,
+ onAgentSelect,
},
ref,
) => {
+ const { enabled: customAgentsEnabled, loading: flagsLoading } = useFeatureFlag('custom_agents');
+
useEffect(() => {
const textarea = ref as React.RefObject;
if (!textarea.current) return;
@@ -147,17 +156,14 @@ export const MessageInput = forwardRef(
messages={messages}
/>
)}
-
+
+
{subscriptionStatus === 'no_subscription' && !isLocalMode() &&
Upgrade for full performance
-
The free tier is severely limited by inferior models; upgrade to experience the true full Suna experience.
@@ -165,15 +171,37 @@ export const MessageInput = forwardRef(
}
+
-
+ ) : (
+
+ )}
+
+
+