Alert Engine
Real-time threshold monitoring with configurable rules.
How It Works
Every data point ingested into VitalClaw is evaluated against all active alert rules. When a threshold is breached, a HealthAlert is generated, stored, and emitted via the event bus.
Default Alert Rules
VitalClaw ships with these pre-configured rules:
| Metric | Condition | Threshold | Severity | Cooldown |
|---|---|---|---|---|
| Heart Rate | above | 120 bpm | warning | 10 min |
| Heart Rate | below | 40 bpm | critical | 10 min |
| Blood Oxygen | below | 90% | critical | 5 min |
| Blood Glucose | above | 250 mg/dL | warning | 30 min |
| Blood Glucose | below | 70 mg/dL | critical | 15 min |
| Body Temperature | above | 38.5°C | warning | 60 min |
Custom Rules
engine.addAlertRule({ id: "custom-rhr-high", metric: "resting_heart_rate", condition: "above", // "above" | "below" | "outside_range" threshold: 90, severity: "warning", // "info" | "warning" | "critical" message: "Resting HR elevated at {value} bpm (threshold: {threshold}).", cooldownMs: 600_000, // 10 minutes }); // Range-based rule engine.addAlertRule({ id: "custom-bp-range", metric: "blood_pressure", condition: "outside_range", threshold: 90, // low end thresholdHigh: 140, // high end severity: "warning", message: "Blood pressure out of range: {value} mmHg.", });
Alert Lifecycle
// 1. Triggered automatically on ingest const alerts = engine.ingest(dataPoints); // 2. Listen for alerts engine.events.on("alert:triggered", (event) => { console.log(event.alert.severity, event.alert.message); }); // 3. Query unacknowledged alerts const pending = engine.getAlerts(true); // 4. Acknowledge engine.acknowledgeAlert(pending[0].id); // 5. Resolved event fires engine.events.on("alert:resolved", (event) => { console.log("Resolved:", event.alertId); });
HealthAlert Schema
interface HealthAlert { id: string; userId: string; severity: "info" | "warning" | "critical"; category: "anomaly" | "threshold" | "trend" | "reminder" | "goal"; metric: MetricType; title: string; message: string; value: number; threshold?: number; timestamp: string; acknowledged: boolean; }