diff --git a/apps/dashboard/src/components/agents/agent-behavior-section.tsx b/apps/dashboard/src/components/agents/agent-behavior-section.tsx
new file mode 100644
index 00000000000..d7da479bf9d
--- /dev/null
+++ b/apps/dashboard/src/components/agents/agent-behavior-section.tsx
@@ -0,0 +1,92 @@
+import { RiExpandUpDownLine } from 'react-icons/ri';
+import { HelpTooltipIndicator } from '@/components/primitives/help-tooltip-indicator';
+import { Switch } from '@/components/primitives/switch';
+
+function SectionHeader({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+function ToggleRow({ label, children }: { label: string; children: React.ReactNode }) {
+ return (
+
+
+ {label}
+
+
+ {children}
+
+ );
+}
+
+function EmojiPickerButton({ emoji }: { emoji: string }) {
+ return (
+
+ );
+}
+
+export function AgentBehaviorSection() {
+ return (
+
+
Agent behavior
+
+ {/*
+ Interrupt mode and response timeout are not supported yet.
+
+
+
+
+
+
+
+
+ 30
+ seconds
+
+
+
+ */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/apps/dashboard/src/components/agents/agent-connected-overview.tsx b/apps/dashboard/src/components/agents/agent-connected-overview.tsx
new file mode 100644
index 00000000000..e2f2b1b8e84
--- /dev/null
+++ b/apps/dashboard/src/components/agents/agent-connected-overview.tsx
@@ -0,0 +1,18 @@
+import type { AgentResponse } from '@/api/agents';
+import { AgentBehaviorSection } from './agent-behavior-section';
+import { ConnectedProvidersSection } from './connected-providers-section';
+import { RecentConversationsSection } from './recent-conversations-section';
+
+type AgentConnectedOverviewProps = {
+ agent: AgentResponse;
+};
+
+export function AgentConnectedOverview({ agent }: AgentConnectedOverviewProps) {
+ return (
+
+ );
+}
diff --git a/apps/dashboard/src/components/agents/agent-overview-tab.tsx b/apps/dashboard/src/components/agents/agent-overview-tab.tsx
index 626bf893105..ef62432661f 100644
--- a/apps/dashboard/src/components/agents/agent-overview-tab.tsx
+++ b/apps/dashboard/src/components/agents/agent-overview-tab.tsx
@@ -1,4 +1,5 @@
import type { AgentResponse } from '@/api/agents';
+import { AgentConnectedOverview } from '@/components/agents/agent-connected-overview';
import { AgentSetupGuide } from '@/components/agents/agent-setup-guide';
import { AgentSidebarWidget } from '@/components/agents/agent-sidebar-widget';
@@ -7,10 +8,12 @@ type AgentOverviewTabProps = {
};
export function AgentOverviewTab({ agent }: AgentOverviewTabProps) {
+ const isBridgeConnected = Boolean(agent.bridgeUrl || (agent.devBridgeActive && agent.devBridgeUrl));
+
return (
-
+ {isBridgeConnected ?
:
}
);
}
diff --git a/apps/dashboard/src/components/agents/agent-setup-guide.tsx b/apps/dashboard/src/components/agents/agent-setup-guide.tsx
index fd1ab8e5234..870dd61c386 100644
--- a/apps/dashboard/src/components/agents/agent-setup-guide.tsx
+++ b/apps/dashboard/src/components/agents/agent-setup-guide.tsx
@@ -1,7 +1,9 @@
import { ChatProviderIdEnum } from '@novu/shared';
+import { useQuery } from '@tanstack/react-query';
import { useCallback, useMemo, useState } from 'react';
import { RiExpandUpDownLine } from 'react-icons/ri';
-import { type AgentResponse } from '@/api/agents';
+import { type AgentResponse, getAgentIntegrationsQueryKey, listAgentIntegrations } from '@/api/agents';
+import { requireEnvironment, useEnvironment } from '@/context/environment/hooks';
import { useFetchIntegrations } from '@/hooks/use-fetch-integrations';
import { cn } from '@/utils/ui';
import { AgentCodeSetupSection } from './agent-code-setup-section';
@@ -23,12 +25,45 @@ function resolveProviderSetupGuide(providerId: string) {
}
}
+function AgentSetupGuideComingSoon() {
+
+ return (
+
+
Coming soon
+
+ In-dashboard setup steps will return here as we expand agent tooling.
+
+
+ );
+}
+
export function AgentSetupGuide({ agent }: AgentSetupGuideProps) {
const [isExpanded, setIsExpanded] = useState(true);
const [selectedIntegrationId, setSelectedIntegrationId] = useState(undefined);
const [isProviderComplete, setIsProviderComplete] = useState(false);
+ const { currentEnvironment } = useEnvironment();
const { integrations } = useFetchIntegrations();
+ const agentIntegrationsQuery = useQuery({
+ queryKey: getAgentIntegrationsQueryKey(currentEnvironment?._id, agent.identifier),
+ queryFn: () =>
+ listAgentIntegrations({
+ environment: requireEnvironment(currentEnvironment, 'No environment selected'),
+ agentIdentifier: agent.identifier,
+ limit: 100,
+ }),
+ enabled: Boolean(currentEnvironment && agent.identifier),
+ });
+
+ const hasConnectedIntegration = useMemo(() => {
+ if (isProviderComplete) return true;
+
+ const links = agentIntegrationsQuery.data?.data;
+ if (!links?.length) return false;
+
+ return links.some((link) => Boolean(link.connectedAt));
+ }, [isProviderComplete, agentIntegrationsQuery.data?.data]);
+
const slackFromAgent = agent.integrations?.find((i) => i.providerId === ChatProviderIdEnum.Slack);
const effectiveIntegrationId = selectedIntegrationId ?? slackFromAgent?.integrationId;
@@ -56,6 +91,8 @@ export function AgentSetupGuide({ agent }: AgentSetupGuideProps) {
setIsProviderComplete(true);
}, []);
+ const isBridgeConnected = Boolean(agent.bridgeUrl || (agent.devBridgeActive && agent.devBridgeUrl));
+
return (
)}
diff --git a/apps/dashboard/src/components/agents/connected-providers-section.tsx b/apps/dashboard/src/components/agents/connected-providers-section.tsx
new file mode 100644
index 00000000000..b8f63d44494
--- /dev/null
+++ b/apps/dashboard/src/components/agents/connected-providers-section.tsx
@@ -0,0 +1,135 @@
+import { providers as novuProviders } from '@novu/shared';
+import { useQuery } from '@tanstack/react-query';
+import { RiAddLine, RiArrowRightLine, RiArrowRightSLine } from 'react-icons/ri';
+import { Link, useLocation } from 'react-router-dom';
+import {
+ type AgentResponse,
+ type AgentIntegrationLink,
+ getAgentIntegrationsQueryKey,
+ listAgentIntegrations,
+} from '@/api/agents';
+import { ProviderIcon } from '@/components/integrations/components/provider-icon';
+import { Skeleton } from '@/components/primitives/skeleton';
+import { requireEnvironment, useEnvironment } from '@/context/environment/hooks';
+import { buildRoute, ROUTES } from '@/utils/routes';
+
+type ConnectedProvidersSectionProps = {
+ agent: AgentResponse;
+};
+
+function ProviderCard({ link }: { link: AgentIntegrationLink }) {
+ const providerMeta = novuProviders.find((p) => p.id === link.integration.providerId);
+ const displayName = providerMeta?.displayName ?? link.integration.name;
+
+ return (
+
+ );
+}
+
+function AddProviderCard({ to }: { to: string }) {
+ return (
+
+
+
+ Add new provider
+
+
+
+ );
+}
+
+function ProviderCardSkeleton() {
+ return (
+
+
+
+
+ );
+}
+
+export function ConnectedProvidersSection({ agent }: ConnectedProvidersSectionProps) {
+ const { currentEnvironment } = useEnvironment();
+ const location = useLocation();
+
+ const integrationsQuery = useQuery({
+ queryKey: getAgentIntegrationsQueryKey(currentEnvironment?._id, agent.identifier),
+ queryFn: () =>
+ listAgentIntegrations({
+ environment: requireEnvironment(currentEnvironment, 'No environment selected'),
+ agentIdentifier: agent.identifier,
+ limit: 100,
+ }),
+ enabled: Boolean(currentEnvironment && agent.identifier),
+ });
+
+ const integrationsTabPath = `${buildRoute(ROUTES.AGENT_DETAILS_TAB, {
+ environmentSlug: currentEnvironment?.slug ?? '',
+ agentIdentifier: encodeURIComponent(agent.identifier),
+ agentTab: 'integrations',
+ })}${location.search}`;
+
+ const links = integrationsQuery.data?.data ?? [];
+ const isLoading = integrationsQuery.isLoading;
+
+ return (
+
+
+
+ Connected providers
+
+
+ Manage integrations
+
+
+
+
+
+
+ {isLoading ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+ {links.map((link) => (
+
+ ))}
+
+ >
+ )}
+
+
+
+ );
+}
diff --git a/apps/dashboard/src/components/agents/recent-conversations-section.tsx b/apps/dashboard/src/components/agents/recent-conversations-section.tsx
new file mode 100644
index 00000000000..3e90d97e7ae
--- /dev/null
+++ b/apps/dashboard/src/components/agents/recent-conversations-section.tsx
@@ -0,0 +1,113 @@
+import { RiArrowRightLine, RiRobot2Line } from 'react-icons/ri';
+import { Link, useLocation } from 'react-router-dom';
+import type { AgentResponse } from '@/api/agents';
+import { useEnvironment } from '@/context/environment/hooks';
+import { buildRoute, ROUTES } from '@/utils/routes';
+
+type RecentConversationsSectionProps = {
+ agent: AgentResponse;
+};
+
+function SkeletonBar({ className }: { className?: string }) {
+ return (
+
+ );
+}
+
+function SkeletonMessageCard({ className }: { className?: string }) {
+ return (
+
+ );
+}
+
+function NovuIcon() {
+ return (
+
+ );
+}
+
+function EmptyStateIllustration() {
+ return (
+
+ );
+}
+
+export function RecentConversationsSection({ agent }: RecentConversationsSectionProps) {
+ const { currentEnvironment } = useEnvironment();
+ const location = useLocation();
+
+ const activityTabPath = `${buildRoute(ROUTES.AGENT_DETAILS_TAB, {
+ environmentSlug: currentEnvironment?.slug ?? '',
+ agentIdentifier: encodeURIComponent(agent.identifier),
+ agentTab: 'activity',
+ })}${location.search}`;
+
+ return (
+
+
+
+ Recent conversations
+
+
+ View all activity
+
+
+
+
+
+
+
+
+ No conversations, agent conversations will appear here once the agent starts responding to messages.
+
+
+
+
+ );
+}