feat(plan-management): implement alert-remediation-plan system v1.3
Some checks failed
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled

- 8 new store modules + matcher + initialResponse + aggregation + remediation + AlertStateMonitor + planGenerator

- alert chain auto-integrated after runInspection: freeze snapshot + generate response + real-time agg + auto-start monitor

- AlertAIPanel component + MonitoringDetailPanel expandable alert list

- PlanManagementPage: full page with list/edit/AI generate/templates/commands
This commit is contained in:
2026-10-30 16:55:55 +08:00
committed by trae-solo-agent
parent 1bb5390263
commit a5744e720b
18 changed files with 4109 additions and 21 deletions

View File

@@ -11,7 +11,11 @@ import {
Clock,
Loader2,
ChevronRight,
ChevronDown,
ChevronUp,
} from 'lucide-react';
import AlertAIPanel from './AlertAIPanel';
import type { DevicePlan, InitialResponse, RemediationRun } from '../../infrastructure/inspection/inspectionApi';
// ============ 类型定义 ============
@@ -49,6 +53,17 @@ export interface DetailPanelProps {
metric?: string;
metricValue?: string;
threshold?: string;
// v1.3 预案管理扩展字段
matched_plan_snapshot?: DevicePlan | null;
initial_response?: InitialResponse | null;
remediation?: RemediationRun | null;
escalation_status?: string | null;
// 处置操作回调
onStartRemediation?: () => void;
onPauseRemediation?: () => void;
onResumeRemediation?: () => void;
onCancelRemediation?: () => void;
onRegenerateResponse?: () => void;
}>;
// 设备详情
deviceDetail?: {
@@ -195,7 +210,9 @@ export default function MonitoringDetailPanel({
</div>
);
// ============ 告警列表渲染 ============
// ============ 告警列表渲染(v1.3:可展开 AI 面板)============
const [expandedAlertId, setExpandedAlertId] = useState<string | null>(null);
const renderAlertList = () => (
<div className="space-y-2">
{alertList.length === 0 && (
@@ -203,29 +220,70 @@ export default function MonitoringDetailPanel({
)}
{alertList.map((a) => {
const lc = levelColors[a.level] || levelColors.info;
const hasAI = !!(a.matched_plan_snapshot || a.initial_response || a.remediation);
const isExpanded = expandedAlertId === a.id || (hasAI && !expandedAlertId);
return (
<div
key={a.id}
className="p-3 rounded-lg border transition-all"
style={{ background: 'rgba(15,23,42,0.6)', borderColor: 'rgba(148,163,184,0.15)' }}
>
<div className="flex items-start justify-between gap-2 mb-1">
<div className="flex items-center gap-2 min-w-0">
<div className={`w-2 h-2 rounded-full ${lc.dot} flex-shrink-0 mt-0.5`} />
<span className={`text-xs font-medium ${lc.text} flex-shrink-0`}>{lc.label}</span>
<div key={a.id} className="rounded-lg border transition-all"
style={{ background: 'rgba(15,23,42,0.6)', borderColor: 'rgba(148,163,184,0.15)' }}>
{/* Header:点击展开 */}
<div
className="p-3 cursor-pointer"
onClick={() => hasAI && setExpandedAlertId(isExpanded ? null : a.id)}
>
<div className="flex items-start justify-between gap-2 mb-1">
<div className="flex items-center gap-2 min-w-0">
<div className={`w-2 h-2 rounded-full ${lc.dot} flex-shrink-0 mt-0.5`} />
<span className={`text-xs font-medium ${lc.text} flex-shrink-0`}>{lc.label}</span>
{hasAI && (
<span className="text-[9px] px-1 py-0.5 rounded bg-cyan-500/15 text-cyan-400 flex-shrink-0">
AI 预案
</span>
)}
{a.escalation_status && (
<span className="text-[9px] px-1 py-0.5 rounded bg-red-500/15 text-red-400 flex-shrink-0">
已升级
</span>
)}
</div>
<div className="flex items-center gap-1 flex-shrink-0">
<span className="text-xs text-muted-foreground">{a.time}</span>
{hasAI && (
isExpanded
? <ChevronUp className="w-3.5 h-3.5 text-muted-foreground" />
: <ChevronDown className="w-3.5 h-3.5 text-muted-foreground" />
)}
</div>
</div>
<div className="text-sm text-foreground mb-1">{a.message}</div>
<div className="text-xs text-muted-foreground">
设备:<span className="text-cyan-400">{a.deviceName}</span>
{a.metric && a.metricValue && (
<span className="ml-3">
{a.metric}: {a.metricValue}
{a.threshold && <span className="text-muted-foreground"> (阈值 {a.threshold})</span>}
</span>
)}
</div>
<span className="text-xs text-muted-foreground flex-shrink-0">{a.time}</span>
</div>
<div className="text-sm text-foreground mb-1">{a.message}</div>
<div className="text-xs text-muted-foreground">
设备:<span className="text-cyan-400">{a.deviceName}</span>
{a.metric && a.metricValue && (
<span className="ml-3">
{a.metric}: {a.metricValue}
{a.threshold && <span className="text-muted-foreground"> (阈值 {a.threshold})</span>}
</span>
)}
</div>
{/* AI 面板展开区 */}
{isExpanded && hasAI && (
<div className="px-3 pb-3 pt-0 border-t border-slate-600/20"
style={{ background: 'rgba(8,15,28,0.4)' }}>
<AlertAIPanel
alertId={a.id}
matchedPlan={a.matched_plan_snapshot}
initialResponse={a.initial_response}
remediation={a.remediation}
onStartRemediation={a.onStartRemediation}
onPauseRemediation={a.onPauseRemediation}
onResumeRemediation={a.onResumeRemediation}
onCancelRemediation={a.onCancelRemediation}
onRegenerateResponse={a.onRegenerateResponse}
/>
</div>
)}
</div>
);
})}