Skip to content

Commit cf9d768

Browse files
committed
chore: polish ui related stuff
1 parent b4666c5 commit cf9d768

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1085
-5591
lines changed

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@hookform/resolvers": "^5.0.1",
2020
"@next/third-parties": "^15.3.1",
2121
"@number-flow/react": "^0.5.7",
22-
"@pipedream/sdk": "^1.7.0",
22+
2323
"@radix-ui/react-accordion": "^1.2.11",
2424
"@radix-ui/react-alert-dialog": "^1.1.11",
2525
"@radix-ui/react-avatar": "^1.1.4",

frontend/src/app/(dashboard)/agents/page.tsx

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ export default function AgentsPage() {
358358
}
359359

360360
const customRequirements = item.mcp_requirements?.filter(req =>
361-
req.custom_type && req.custom_type !== 'pipedream'
361+
req.custom_type
362362
) || [];
363363
const missingCustomConfigs = customRequirements.filter(req =>
364364
!customMcpConfigs || !customMcpConfigs[req.qualified_name] ||
@@ -371,20 +371,6 @@ export default function AgentsPage() {
371371
return;
372372
}
373373

374-
const pipedreamRequirements = item.mcp_requirements?.filter(req =>
375-
req.custom_type === 'pipedream'
376-
) || [];
377-
const missingPipedreamConfigs = pipedreamRequirements.filter(req =>
378-
!customMcpConfigs || !customMcpConfigs[req.qualified_name] ||
379-
!customMcpConfigs[req.qualified_name].profile_id
380-
);
381-
382-
if (missingPipedreamConfigs.length > 0) {
383-
const missingNames = missingPipedreamConfigs.map(req => req.display_name).join(', ');
384-
toast.error(`Please select Pipedream profiles for: ${missingNames}`);
385-
return;
386-
}
387-
388374
const result = await installTemplateMutation.mutateAsync({
389375
template_id: item.template_id,
390376
instance_name: instanceName,
@@ -566,7 +552,6 @@ export default function AgentsPage() {
566552
</div>
567553
</div>
568554
<div className="container mx-auto max-w-7xl px-4 py-2">
569-
{/* Fixed height container to prevent layout shifts on tab change */}
570555
<div className="w-full min-h-[calc(100vh-300px)]">
571556
{activeTab === "my-agents" && (
572557
<MyAgentsTab

frontend/src/app/(dashboard)/projects/[projectId]/thread/[threadId]/page.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ import { UnifiedMessage, ApiMessageType, ToolCallInput, Project } from '../_type
2727
import { useThreadData, useToolCalls, useBilling, useKeyboardShortcuts } from '../_hooks';
2828
import { ThreadError, UpgradeDialog, ThreadLayout } from '../_components';
2929
import { useVncPreloader } from '@/hooks/useVncPreloader';
30-
import { useThreadAgent } from '@/hooks/react-query/agents/use-agents';
30+
import { useThreadAgent, useAgents } from '@/hooks/react-query/agents/use-agents';
3131
import { AgentRunLimitDialog } from '@/components/thread/agent-run-limit-dialog';
32+
import { useAgentSelection } from '@/lib/stores/agent-selection-store';
3233

3334
export default function ThreadPage({
3435
params,
@@ -52,7 +53,17 @@ export default function ThreadPage({
5253
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
5354
const [debugMode, setDebugMode] = useState(false);
5455
const [initialPanelOpenAttempted, setInitialPanelOpenAttempted] = useState(false);
55-
const [selectedAgentId, setSelectedAgentId] = useState<string | undefined>(undefined);
56+
// Use Zustand store for agent selection persistence
57+
const {
58+
selectedAgentId,
59+
setSelectedAgent,
60+
initializeFromAgents,
61+
getCurrentAgent,
62+
isSunaAgent
63+
} = useAgentSelection();
64+
65+
const { data: agentsResponse } = useAgents();
66+
const agents = agentsResponse?.agents || [];
5667
const [isSidePanelAnimating, setIsSidePanelAnimating] = useState(false);
5768
const [userInitiatedRun, setUserInitiatedRun] = useState(false);
5869
const [showScrollToBottom, setShowScrollToBottom] = useState(false);
@@ -134,12 +145,13 @@ export default function ThreadPage({
134145
const agent = threadAgentData?.agent;
135146
const workflowId = threadQuery.data?.metadata?.workflow_id;
136147

137-
// Set initial selected agent from thread data
148+
// Initialize agent selection from thread data and agents list
138149
useEffect(() => {
139-
if (threadAgentData?.agent && !selectedAgentId) {
140-
setSelectedAgentId(threadAgentData.agent.agent_id);
150+
if (agents.length > 0) {
151+
const threadAgentId = threadAgentData?.agent?.agent_id;
152+
initializeFromAgents(agents, threadAgentId);
141153
}
142-
}, [threadAgentData, selectedAgentId]);
154+
}, [threadAgentData, agents, initializeFromAgents]);
143155

144156
const { data: subscriptionData } = useSubscription();
145157
const subscriptionStatus: SubscriptionStatus = subscriptionData?.status === 'active'
@@ -735,7 +747,7 @@ export default function ThreadPage({
735747
messages={messages}
736748
agentName={agent && agent.name}
737749
selectedAgentId={selectedAgentId}
738-
onAgentSelect={setSelectedAgentId}
750+
onAgentSelect={setSelectedAgent}
739751
toolCalls={toolCalls}
740752
toolCallIndex={currentToolIndex}
741753
showToolPreview={!isSidePanelOpen && toolCalls.length > 0}

frontend/src/components/agents/composio/composio-registry.tsx

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { Badge } from '@/components/ui/badge';
44
import { Skeleton } from '@/components/ui/skeleton';
55
import { Button } from '@/components/ui/button';
66
import { ScrollArea } from '@/components/ui/scroll-area';
7-
import { Search, Zap, X, Settings, ChevronDown, ChevronUp, Loader2 } from 'lucide-react';
8-
import { useComposioToolkits, useComposioCategories, useComposioToolkitsInfinite } from '@/hooks/react-query/composio/use-composio';
7+
import { Search, Zap, X, Settings, ChevronDown, ChevronUp, Loader2, Server } from 'lucide-react';
8+
import { useComposioCategories, useComposioToolkitsInfinite } from '@/hooks/react-query/composio/use-composio';
99
import { useComposioProfiles } from '@/hooks/react-query/composio/use-composio-profiles';
1010
import { useAgent, useUpdateAgent } from '@/hooks/react-query/agents/use-agents';
1111
import { ComposioConnector } from './composio-connector';
@@ -16,7 +16,7 @@ import { cn } from '@/lib/utils';
1616
import { useQueryClient } from '@tanstack/react-query';
1717
import { AgentSelector } from '../../thread/chat-input/agent-selector';
1818
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
19-
import { Switch } from '@/components/ui/switch';
19+
import { CustomMCPDialog } from '../mcp/custom-mcp-dialog';
2020

2121
const CATEGORY_EMOJIS: Record<string, string> = {
2222
'popular': '🔥',
@@ -76,7 +76,6 @@ const getAgentConnectedApps = (
7676
return connectedApps;
7777
};
7878

79-
// Helper function to check if an app is connected to the agent
8079
const isAppConnectedToAgent = (
8180
agent: any,
8281
appSlug: string,
@@ -289,6 +288,7 @@ export const ComposioRegistry: React.FC<ComposioRegistryProps> = ({
289288
const [showConnectedApps, setShowConnectedApps] = useState(true);
290289
const [showToolsManager, setShowToolsManager] = useState(false);
291290
const [selectedConnectedApp, setSelectedConnectedApp] = useState<ConnectedApp | null>(null);
291+
const [showCustomMCPDialog, setShowCustomMCPDialog] = useState(false);
292292

293293
const [internalSelectedAgentId, setInternalSelectedAgentId] = useState<string | undefined>(selectedAgentId);
294294
const queryClient = useQueryClient();
@@ -404,6 +404,41 @@ export const ComposioRegistry: React.FC<ComposioRegistryProps> = ({
404404
}
405405
};
406406

407+
const handleCustomMCPSave = async (customConfig: any): Promise<void> => {
408+
if (!currentAgentId) {
409+
throw new Error('Please select an agent first');
410+
}
411+
412+
// Create MCP configuration for agent
413+
const mcpConfig = {
414+
name: customConfig.name || 'Custom MCP',
415+
type: customConfig.type || 'sse',
416+
config: customConfig.config || {},
417+
enabledTools: customConfig.enabledTools || [],
418+
};
419+
420+
// Get current custom MCPs from agent
421+
const currentCustomMcps = agent?.custom_mcps || [];
422+
const updatedCustomMcps = [...currentCustomMcps, mcpConfig];
423+
424+
// Return a promise that resolves/rejects based on the mutation result
425+
return new Promise((resolve, reject) => {
426+
updateAgent({
427+
agentId: currentAgentId,
428+
custom_mcps: updatedCustomMcps
429+
}, {
430+
onSuccess: () => {
431+
toast.success(`Custom MCP "${customConfig.name}" added successfully`);
432+
queryClient.invalidateQueries({ queryKey: ['agent', currentAgentId] });
433+
resolve();
434+
},
435+
onError: (error: any) => {
436+
reject(new Error(error.message || 'Failed to add custom MCP'));
437+
}
438+
});
439+
});
440+
};
441+
407442
const categories = categoriesData?.categories || [];
408443

409444
return (
@@ -484,6 +519,17 @@ export const ComposioRegistry: React.FC<ComposioRegistryProps> = ({
484519
isSunaAgent={agent?.metadata?.is_suna_default}
485520
/>
486521
)}
522+
{mode !== 'profile-only' && currentAgentId && (
523+
<Button
524+
variant="outline"
525+
size="sm"
526+
onClick={() => setShowCustomMCPDialog(true)}
527+
className="flex items-center gap-2"
528+
>
529+
<Server className="h-4 w-4" />
530+
Add Custom MCP
531+
</Button>
532+
)}
487533
</div>
488534
</div>
489535

@@ -663,6 +709,11 @@ export const ComposioRegistry: React.FC<ComposioRegistryProps> = ({
663709
}}
664710
/>
665711
)}
712+
<CustomMCPDialog
713+
open={showCustomMCPDialog}
714+
onOpenChange={setShowCustomMCPDialog}
715+
onSave={handleCustomMCPSave}
716+
/>
666717
</div>
667718
);
668719
};

frontend/src/components/agents/installation/streamlined-install-dialog.tsx

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,6 @@ export const StreamlinedInstallDialog: React.FC<StreamlinedInstallDialogProps> =
5353
if (!item?.mcp_requirements) return [];
5454

5555
const steps: SetupStep[] = [];
56-
57-
item.mcp_requirements
58-
.filter(req => req.custom_type === 'pipedream')
59-
.forEach(req => {
60-
const app_slug = req.qualified_name.startsWith('pipedream:')
61-
? req.qualified_name.substring('pipedream:'.length)
62-
: req.qualified_name;
63-
64-
steps.push({
65-
id: req.qualified_name,
66-
title: `Connect ${req.display_name}`,
67-
description: `Select an existing ${req.display_name} profile or create a new one`,
68-
type: 'pipedream_profile',
69-
service_name: req.display_name,
70-
qualified_name: req.qualified_name,
71-
app_slug: app_slug
72-
});
73-
});
74-
7556
item.mcp_requirements
7657
.filter(req => req.custom_type === 'composio')
7758
.forEach(req => {
@@ -110,7 +91,7 @@ export const StreamlinedInstallDialog: React.FC<StreamlinedInstallDialogProps> =
11091
});
11192

11293
item.mcp_requirements
113-
.filter(req => req.custom_type && req.custom_type !== 'pipedream' && req.custom_type !== 'composio')
94+
.filter(req => req.custom_type && req.custom_type !== 'composio')
11495
.forEach(req => {
11596
steps.push({
11697
id: req.qualified_name,
@@ -174,7 +155,6 @@ export const StreamlinedInstallDialog: React.FC<StreamlinedInstallDialogProps> =
174155

175156
switch (step.type) {
176157
case 'credential_profile':
177-
case 'pipedream_profile':
178158
case 'composio_profile':
179159
return !!profileMappings[step.qualified_name];
180160
case 'custom_server':
@@ -205,18 +185,7 @@ export const StreamlinedInstallDialog: React.FC<StreamlinedInstallDialogProps> =
205185
const finalCustomConfigs = { ...customMcpConfigs };
206186

207187
setupSteps.forEach(step => {
208-
if (step.type === 'pipedream_profile') {
209-
const profileId = profileMappings[step.qualified_name];
210-
if (profileId) {
211-
finalCustomConfigs[step.qualified_name] = {
212-
url: 'https://remote.mcp.pipedream.net',
213-
headers: {
214-
'x-pd-app-slug': step.app_slug,
215-
},
216-
profile_id: profileId
217-
};
218-
}
219-
} else if (step.type === 'composio_profile') {
188+
if (step.type === 'composio_profile') {
220189
const profileId = profileMappings[step.qualified_name];
221190
if (profileId) {
222191
finalCustomConfigs[step.qualified_name] = {
@@ -298,7 +267,7 @@ export const StreamlinedInstallDialog: React.FC<StreamlinedInstallDialogProps> =
298267
</div>
299268

300269
<div>
301-
{(currentStepData.type === 'credential_profile' || currentStepData.type === 'pipedream_profile' || currentStepData.type === 'composio_profile') && (
270+
{(currentStepData.type === 'credential_profile' || currentStepData.type === 'composio_profile') && (
302271
<ProfileConnector
303272
step={currentStepData}
304273
selectedProfileId={profileMappings[currentStepData.qualified_name]}

0 commit comments

Comments
 (0)