mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
fix: dispose creates inbound Call record, webhook enriches — eliminates UCID mismatch + timing race
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Root cause: CDR webhook fires 5s after dispose, stores monitorUCID. Frontend has agent-side UCID from SIP. They never matched → disposition not persisted, agent call history empty. Fix: - Dispose endpoint now creates Call records for ALL answered calls (inbound + outbound), not just outbound. Record gets agent-side UCID + correct disposition immediately. - Webhook checks if Call record already exists (by agent UCID via monitorUCID→agentUCID mapping). If found, enriches with recording URL, agent chain name, CDR timing. If not found, creates as fallback. - SupervisorService stores UCID mapping from real-time events (which carry both UCIDs). Auto-expires after 10 minutes. Verified: UCID-MAP logged, dispose creates record, webhook enriches without duplicating. DB shows correct agent UCID + disposition + recording. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,8 @@ export class SupervisorService implements OnModuleInit {
|
||||
private readonly activeCalls = new Map<string, ActiveCall>();
|
||||
private readonly agentStates = new Map<string, AgentStateEntry>();
|
||||
private readonly acwTimers = new Map<string, NodeJS.Timeout>();
|
||||
// monitorUCID → agentUCID. Real-time events carry both; CDR webhook only has monitorUCID.
|
||||
private readonly ucidMap = new Map<string, string>();
|
||||
readonly agentStateSubject = new Subject<{ agentId: string; state: AgentOzonetelState | string; timestamp: string }>();
|
||||
readonly activeCallSubject = new Subject<{ type: 'update' | 'remove'; call?: ActiveCall; ucid: string }>();
|
||||
// Worklist update stream — emitted when a missed call is created or
|
||||
@@ -78,9 +80,14 @@ export class SupervisorService implements OnModuleInit {
|
||||
}
|
||||
}
|
||||
|
||||
resolveAgentUcid(monitorUcid: string): string | null {
|
||||
return this.ucidMap.get(monitorUcid) ?? null;
|
||||
}
|
||||
|
||||
handleCallEvent(event: any) {
|
||||
const action = event.action;
|
||||
const ucid = event.ucid ?? event.monitorUCID;
|
||||
const monitorUcid = event.monitor_ucid ?? event.monitorUCID;
|
||||
const agentId = event.agent_id ?? event.agentID;
|
||||
const callerNumber = event.caller_id ?? event.callerID;
|
||||
const callType = event.call_type ?? event.Type;
|
||||
@@ -89,6 +96,12 @@ export class SupervisorService implements OnModuleInit {
|
||||
|
||||
if (!ucid) return;
|
||||
|
||||
if (monitorUcid && ucid !== monitorUcid) {
|
||||
this.ucidMap.set(monitorUcid, ucid);
|
||||
this.logger.log(`[UCID-MAP] monitor=${monitorUcid} → agent=${ucid}`);
|
||||
setTimeout(() => this.ucidMap.delete(monitorUcid), 600_000);
|
||||
}
|
||||
|
||||
if (action === 'Answered' || action === 'Calling') {
|
||||
// Don't show calls for offline agents (ghost calls)
|
||||
const agentState = this.agentStates.get(agentId);
|
||||
|
||||
Reference in New Issue
Block a user