feat: ZeroClaw compliance agent — document analysis + role assignment + email

Add autonomous compliance agent that fetches web documents (cookie banners,
privacy policies), classifies them via Qwen/Ollama, assesses DSGVO compliance,
assigns to the responsible role, and sends notification emails.

Components:
- ZeroClaw SOP (6-step workflow: fetch, classify, assess, summarize, assign, notify)
- Backend: /api/compliance/agent/analyze (combined endpoint)
- Backend: /api/compliance/agent/notify (standalone email)
- Frontend: /sdk/agent page (Manager UI with URL input + results)
- Helper scripts + E2E test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-27 23:27:25 +02:00
parent f528b8e7a9
commit 0c0dd4e3a6
16 changed files with 1095 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
'use client'
import { useState } from 'react'
export interface AnalysisResult {
url: string
classification: string
risk_level: string
risk_score: number
escalation_level: string
responsible_role: string
findings: string[]
required_controls: string[]
summary: string
email_status: string
analyzed_at: string
}
const ESCALATION_ROLES: Record<string, string> = {
E0: 'Kein Handlungsbedarf',
E1: 'Teamleitung Datenschutz',
E2: 'Datenschutzbeauftragter (DSB)',
E3: 'DSB + Rechtsabteilung',
}
const SDK_HEADERS = {
'Content-Type': 'application/json',
'X-Tenant-ID': '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e',
'X-User-ID': '00000000-0000-0000-0000-000000000001',
}
export function useAgentAnalysis() {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [result, setResult] = useState<AnalysisResult | null>(null)
const [history, setHistory] = useState<AnalysisResult[]>([])
async function analyze(url: string) {
setLoading(true)
setError(null)
setResult(null)
try {
// Step 1: Fetch and classify
const fetchRes = await fetch('/api/sdk/v1/agent/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url }),
})
if (!fetchRes.ok) {
throw new Error(`Analyse fehlgeschlagen: ${fetchRes.status}`)
}
const data = await fetchRes.json()
const analysisResult: AnalysisResult = {
url,
classification: data.classification || 'unknown',
risk_level: data.risk_level || 'unknown',
risk_score: data.risk_score || 0,
escalation_level: data.escalation_level || 'E0',
responsible_role: ESCALATION_ROLES[data.escalation_level] || ESCALATION_ROLES.E0,
findings: data.findings || [],
required_controls: data.required_controls || [],
summary: data.summary || '',
email_status: data.email_status || 'pending',
analyzed_at: new Date().toISOString(),
}
setResult(analysisResult)
setHistory(prev => [analysisResult, ...prev].slice(0, 20))
} catch (e) {
setError(e instanceof Error ? e.message : 'Unbekannter Fehler')
} finally {
setLoading(false)
}
}
return { analyze, loading, error, result, history }
}