feat(performance-alerts): rules-engine-driven alerts, persisted as PerformanceAlert

Phase A+B of the alerts overhaul:

- New PerformanceFactsProvider exposes agent.idleMinutes (from
  AgentSession), agent.busyMinutes, agent.totalCallsToday,
  agent.bookedCallsToday, agent.conversionPercent
- Implement EscalateActionHandler (was a stub): persists a
  PerformanceAlert row, dedupes per agent+type+IST date so a 5-min
  cron can't spam, updates value if it changes
- New PerformanceConsumer: setInterval every 5 min, reads on_schedule
  rules referencing agent.* facts, evaluates per agent, dispatches
  escalate actions
- Two starter rules in hospital-starter.json: excessive-idle (>60min)
  and low-conversion (<15% with >10 calls today). NPS deferred — no
  source signal exists yet
- New PerformanceAlertsController: GET /api/supervisor/performance-alerts
  (active list), POST /:id/dismiss, POST /dismiss-all
- Rules engine now injects EscalateActionHandler via DI so the action
  has access to PlatformGraphqlService for persistence

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-15 09:02:02 +05:30
parent 5b40f49b65
commit 8dcfa5a72f
8 changed files with 451 additions and 10 deletions

View File

@@ -84,6 +84,51 @@
"trigger": { "type": "on_schedule", "interval": "5m" },
"conditions": { "all": [{ "fact": "call.slaBreached", "operator": "equal", "value": true }, { "fact": "call.callbackStatus", "operator": "equal", "value": "PENDING_CALLBACK" }] },
"action": { "type": "escalate", "params": { "channel": "notification", "recipients": "supervisor", "message": "SLA breached — no callback attempted", "severity": "critical" } }
},
{
"ruleType": "automation",
"name": "Excessive idle time",
"description": "Agent has been idle for more than the configured threshold today",
"enabled": true,
"priority": 2,
"trigger": { "type": "on_schedule", "interval": "5m" },
"conditions": { "all": [{ "fact": "agent.idleMinutes", "operator": "greaterThan", "value": 60 }] },
"action": {
"type": "escalate",
"params": {
"channel": "notification",
"recipients": "supervisor",
"message": "Excessive Idle Time",
"severity": "warning",
"alertType": "EXCESSIVE_IDLE",
"valueFact": "agent.idleMinutes"
}
}
},
{
"ruleType": "automation",
"name": "Low conversion rate",
"description": "Agent's conversion (booked/total) is below the workspace floor",
"enabled": true,
"priority": 3,
"trigger": { "type": "on_schedule", "interval": "5m" },
"conditions": {
"all": [
{ "fact": "agent.conversionPercent", "operator": "lessThan", "value": 15 },
{ "fact": "agent.totalCallsToday", "operator": "greaterThan", "value": 10 }
]
},
"action": {
"type": "escalate",
"params": {
"channel": "notification",
"recipients": "supervisor",
"message": "Low Conversion",
"severity": "warning",
"alertType": "LOW_CONVERSION",
"valueFact": "agent.conversionPercent"
}
}
}
]
}