// src/rules-engine/facts/call-facts.provider.ts import type { FactProvider, FactValue } from '../types/fact.types'; import type { PriorityConfig } from '../types/rule.types'; export class CallFactsProvider implements FactProvider { name = 'call'; async resolveFacts(call: any, priorityConfig?: PriorityConfig): Promise> { const taskType = this.inferTaskType(call); const slaMinutes = priorityConfig?.taskWeights[taskType]?.slaMinutes ?? 1440; const createdAt = call.createdAt ? new Date(call.createdAt).getTime() : Date.now(); const elapsedMinutes = Math.round((Date.now() - createdAt) / 60000); const slaElapsedPercent = Math.round((elapsedMinutes / slaMinutes) * 100); return { 'call.direction': call.callDirection ?? call.direction ?? null, 'call.status': call.callStatus ?? null, 'call.disposition': call.disposition ?? null, 'call.durationSeconds': call.durationSeconds ?? call.durationSec ?? 0, 'call.callbackStatus': call.callbackStatus ?? call.callbackStatus ?? null, 'call.slaElapsedPercent': slaElapsedPercent, 'call.slaBreached': slaElapsedPercent > 100, 'call.missedCount': call.missedCallCount ?? call.missedCount ?? 0, 'call.taskType': taskType, }; } private inferTaskType(call: any): string { if (call.callStatus === 'MISSED' || call.type === 'missed') return 'missed_call'; if (call.followUpType === 'CALLBACK' || call.type === 'callback') return 'follow_up'; if (call.type === 'follow-up') return 'follow_up'; if (call.contactAttempts >= 3) return 'attempt_3'; if (call.contactAttempts >= 2) return 'attempt_2'; if (call.campaignId || call.type === 'lead') return 'campaign_lead'; return 'campaign_lead'; } } // Exported scoring functions — used by both sidecar and frontend (via scoring.ts) export function computeSlaMultiplier(slaElapsedPercent: number): number { const elapsed = slaElapsedPercent / 100; if (elapsed > 1) return 1.0 + (elapsed - 1) * 0.5; return Math.pow(elapsed, 1.6); } export function computeSlaStatus(slaElapsedPercent: number): 'low' | 'medium' | 'high' | 'critical' { if (slaElapsedPercent > 100) return 'critical'; if (slaElapsedPercent >= 80) return 'high'; if (slaElapsedPercent >= 50) return 'medium'; return 'low'; }