import { useCallback, useEffect, useMemo, useState } from 'react'; import { Lock, Search, Shield, ShieldAlert, ShieldCheck, ToggleLeft, ToggleRight } from 'lucide-react'; import type { SafetyPermissionScope, SafetyRiskLevel } from '@/app-types'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { ScrollArea } from '@/components/ui/scroll-area'; import { loadSafetyScopes, saveSafetyScopes } from '@/lib/safety-policy'; type SafetyPageProps = { gatewayConnected: boolean; projectId?: string; projectTitle?: string; }; const RISK_META: Record = { low: { label: 'Low', badge: 'border-emerald-500/35 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300', section: 'text-emerald-700 dark:text-emerald-300', }, medium: { label: 'Medium', badge: 'border-amber-500/40 bg-amber-500/12 text-amber-800 dark:text-amber-300', section: 'text-amber-700 dark:text-amber-300', }, high: { label: 'High', badge: 'border-orange-500/35 bg-orange-500/12 text-orange-700 dark:text-orange-300', section: 'text-orange-700 dark:text-orange-300', }, critical: { label: 'Critical', badge: 'border-destructive/35 bg-destructive/10 text-destructive', section: 'text-destructive', }, }; const RISK_ORDER: SafetyRiskLevel[] = ['critical', 'high', 'medium', 'low']; export function SafetyPage({ gatewayConnected, projectId, projectTitle }: SafetyPageProps) { const [scopes, setScopes] = useState(() => loadSafetyScopes(projectId)); const [filterQuery, setFilterQuery] = useState(''); const [riskFilter, setRiskFilter] = useState('all'); useEffect(() => { setScopes(loadSafetyScopes(projectId)); }, [projectId]); const persistScopes = useCallback( (updated: SafetyPermissionScope[]) => { setScopes(updated); saveSafetyScopes(updated, projectId); }, [projectId], ); const toggleScopeEnabled = useCallback( (id: string) => { const updated = scopes.map((scope) => (scope.id === id ? { ...scope, enabled: !scope.enabled } : scope)); persistScopes(updated); }, [persistScopes, scopes], ); const toggleScopeApproval = useCallback( (id: string) => { const updated = scopes.map((scope) => (scope.id === id ? { ...scope, requiresApproval: !scope.requiresApproval } : scope)); persistScopes(updated); }, [persistScopes, scopes], ); const stats = useMemo(() => { const enabled = scopes.filter((scope) => scope.enabled).length; const approvals = scopes.filter((scope) => scope.enabled && scope.requiresApproval).length; const highRiskEnabled = scopes.filter((scope) => scope.enabled && (scope.riskLevel === 'high' || scope.riskLevel === 'critical')).length; return { enabled, approvals, highRiskEnabled }; }, [scopes]); const filteredScopes = useMemo(() => { const query = filterQuery.trim().toLowerCase(); return scopes .filter((scope) => (riskFilter === 'all' ? true : scope.riskLevel === riskFilter)) .filter((scope) => { if (!query) { return true; } return scope.name.toLowerCase().includes(query) || scope.description.toLowerCase().includes(query); }); }, [filterQuery, riskFilter, scopes]); const groupedScopes = useMemo(() => { const groups: Record = { low: [], medium: [], high: [], critical: [], }; for (const scope of filteredScopes) { groups[scope.riskLevel].push(scope); } return groups; }, [filteredScopes]); return (

Safety and approvals

{projectId ? projectTitle?.trim() || 'Selected project' : 'Global'} {gatewayConnected ? 'Gateway connected' : 'Gateway offline'}

Define exactly what the agent can do, and whether each action runs automatically or needs human approval.

Rule behavior: Enabled + Automatic = executes immediately. Enabled + Approval required = asks first. Disabled = blocked.

Enabled rules

{stats.enabled}

Need approval

{stats.approvals}

High/critical enabled

0 ? 'text-destructive' : 'text-foreground'}`}>{stats.highRiskEnabled}

setFilterQuery(event.target.value)} placeholder="Search permissions..." className="h-7 border-0 bg-transparent px-0 text-[12px] shadow-none focus-visible:ring-0" /> {filterQuery.trim() ? ( ) : null}
{(['all', 'low', 'medium', 'high', 'critical'] as const).map((level) => ( ))}
{filteredScopes.length === 0 ? (

No permissions match your current filter.

) : null} {RISK_ORDER.map((riskLevel) => { const scopesInGroup = groupedScopes[riskLevel]; if (scopesInGroup.length === 0) { return null; } const risk = RISK_META[riskLevel]; return (
{risk.label} risk ({scopesInGroup.length})
{scopesInGroup.map((scope) => (

{scope.name}

{scope.description}

{scope.enabled ? 'Enabled' : 'Disabled'} {scope.requiresApproval ? 'Approval required' : 'Automatic'} {risk.label}
))}
); })}
); }