diff --git a/frontend-modern/src/App.tsx b/frontend-modern/src/App.tsx
index 9b01f6fc9..d34d822cf 100644
--- a/frontend-modern/src/App.tsx
+++ b/frontend-modern/src/App.tsx
@@ -46,6 +46,7 @@ import SettingsIcon from 'lucide-solid/icons/settings';
import NetworkIcon from 'lucide-solid/icons/network';
import Maximize2Icon from 'lucide-solid/icons/maximize-2';
import Minimize2Icon from 'lucide-solid/icons/minimize-2';
+import { PulsePatrolLogo } from '@/components/Brand/PulsePatrolLogo';
import { TokenRevealDialog } from './components/TokenRevealDialog';
import { useAlertsActivation } from './stores/alertsActivation';
import { UpdateProgressModal } from './components/UpdateProgressModal';
@@ -53,6 +54,7 @@ import type { UpdateStatus } from './api/updates';
import { AIChat } from './components/AI/Chat';
import { AIStatusIndicator } from './components/AI/AIStatusIndicator';
import { aiChatStore } from './stores/aiChat';
+import { getPatrolStatus } from './api/patrol';
import { useResourcesAsLegacy } from './hooks/useResources';
import { updateSystemSettingsFromResponse, markSystemSettingsLoadedWithDefaults } from './stores/systemSettings';
import { initKioskMode, isKioskMode, setKioskMode, subscribeToKioskMode } from './utils/url';
@@ -83,6 +85,9 @@ const HostsOverview = lazy(() =>
default: module.HostsOverview,
})),
);
+const AIIntelligencePage = lazy(() =>
+ import('./pages/AIIntelligence').then((module) => ({ default: module.AIIntelligence })),
+);
// Enhanced store type with proper typing
@@ -962,6 +967,7 @@ function App() {
} />
+
);
@@ -1037,6 +1043,17 @@ function AppLayout(props: {
const navigate = useNavigate();
const location = useLocation();
+ // Track patrol license status for Pro badge
+ const [patrolLicenseRequired, setPatrolLicenseRequired] = createSignal(false);
+ onMount(async () => {
+ try {
+ const status = await getPatrolStatus();
+ setPatrolLicenseRequired(status.license_required ?? false);
+ } catch {
+ // Ignore errors - default to not showing badge
+ }
+ });
+
const readSeenPlatforms = (): Record => {
if (typeof window === 'undefined') return {};
try {
@@ -1101,6 +1118,7 @@ function AppLayout(props: {
if (path.startsWith('/hosts')) return 'hosts';
if (path.startsWith('/servers')) return 'hosts'; // Legacy redirect
if (path.startsWith('/alerts')) return 'alerts';
+ if (path.startsWith('/ai')) return 'ai';
if (path.startsWith('/settings')) return 'settings';
return 'proxmox';
};
@@ -1223,11 +1241,11 @@ function AppLayout(props: {
scopes.includes('*') || scopes.includes('settings:read');
const tabs: Array<{
- id: 'alerts' | 'settings';
+ id: 'alerts' | 'ai' | 'settings';
label: string;
route: string;
tooltip: string;
- badge: 'update' | null;
+ badge: 'update' | 'pro' | null;
count: number | undefined;
breakdown: { warning: number; critical: number } | undefined;
icon: JSX.Element;
@@ -1242,6 +1260,16 @@ function AppLayout(props: {
breakdown,
icon: ,
},
+ {
+ id: 'ai',
+ label: 'Patrol',
+ route: '/ai',
+ tooltip: 'Pulse Patrol monitoring and analysis',
+ badge: patrolLicenseRequired() ? 'pro' : null,
+ count: undefined,
+ breakdown: undefined,
+ icon: ,
+ },
];
// Only show settings tab if user has access
@@ -1484,6 +1512,11 @@ function AppLayout(props: {
+
+
+ Pro
+
+
);
}}
diff --git a/frontend-modern/src/api/agentProfiles.ts b/frontend-modern/src/api/agentProfiles.ts
index cc666c6d1..4ee1f02fc 100644
--- a/frontend-modern/src/api/agentProfiles.ts
+++ b/frontend-modern/src/api/agentProfiles.ts
@@ -231,7 +231,7 @@ export class AgentProfilesAPI {
if (!response.ok) {
const text = await response.text();
if (response.status === 503) {
- throw new Error('AI service is not available. Please check AI settings.');
+ throw new Error('Pulse Assistant service is not available. Please check Pulse Assistant settings.');
}
throw new Error(text || `Failed to get suggestion: ${response.status}`);
}
diff --git a/frontend-modern/src/components/Backups/UnifiedBackups.tsx b/frontend-modern/src/components/Backups/UnifiedBackups.tsx
index 935ffb7d8..6eeaf93bc 100644
--- a/frontend-modern/src/components/Backups/UnifiedBackups.tsx
+++ b/frontend-modern/src/components/Backups/UnifiedBackups.tsx
@@ -1163,7 +1163,7 @@ const UnifiedBackups: Component = () => {
}
title="No backup sources configured"
- description="Add a Proxmox VE or PBS node in the Settings tab to start monitoring backups."
+ description="Install the Pulse agent for extra capabilities (temperature monitoring and Pulse Patrol automation), or add a node via API token in Settings → Proxmox."
actions={
+
+
+
+
+
+ Proxmox nodes can be added here with the unified agent for extra capabilities like temperature monitoring and Pulse Patrol automation (auto-creates the API token and links the node).
+
- Pulse commands enabled — The agent will accept diagnostic and fix commands from Pulse AI features.
+ Pulse commands enabled — The agent will accept diagnostic and fix commands from Pulse Patrol features.
- Requires API key configuration in Settings → AI after setup
+ Requires API key configuration in Settings → Pulse Assistant after setup
diff --git a/frontend-modern/src/components/Storage/Storage.tsx b/frontend-modern/src/components/Storage/Storage.tsx
index af7501041..a78a9fbe2 100644
--- a/frontend-modern/src/components/Storage/Storage.tsx
+++ b/frontend-modern/src/components/Storage/Storage.tsx
@@ -754,7 +754,7 @@ const Storage: Component = () => {
}
title="No storage configured"
- description="Add a Proxmox VE or PBS node in the Settings tab to start monitoring storage."
+ description="Install the Pulse agent for extra capabilities (temperature monitoring and Pulse Patrol automation), or add a node via API token in Settings → Proxmox."
actions={