mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
fix: persist LOGIN events for session rollup — fixes zero dashboard metrics
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
History event persistence was gated behind if(mapped), but login returns null for state (UI waits for release). LOGIN events were never written to AgentEvent table → rollup computed 0 for loginDuration → idle/pause/ wrap all zero. Moved history persistence outside the state mapping gate. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -176,26 +176,30 @@ export class SupervisorService implements OnModuleInit {
|
|||||||
|
|
||||||
const priorState = this.agentStates.get(agentId)?.state;
|
const priorState = this.agentStates.get(agentId)?.state;
|
||||||
const mapped = this.mapOzonetelAction(action, eventData, pauseReason);
|
const mapped = this.mapOzonetelAction(action, eventData, pauseReason);
|
||||||
|
|
||||||
|
// Persist to AgentEvent table regardless of state mapping.
|
||||||
|
// login returns null for state (UI waits for release/ready) but
|
||||||
|
// the history pipeline needs LOGIN to compute loginDuration.
|
||||||
|
const historyEventType = this.mapToHistoryEventType(action, priorState);
|
||||||
|
if (historyEventType) {
|
||||||
|
const resolvedPauseReason = (pauseReason || eventData || '') || null;
|
||||||
|
this.logger.log(`[AGENT-HISTORY] ${agentId} action=${action} → eventType=${historyEventType} priorState=${priorState ?? 'none'} mapped=${mapped ?? 'null'}`);
|
||||||
|
this.history.persistAgentEvent({
|
||||||
|
ozonetelAgentId: agentId,
|
||||||
|
eventType: historyEventType,
|
||||||
|
eventAt: this.parseOzonetelTime(eventTime),
|
||||||
|
pauseReason: historyEventType === 'PAUSE' ? resolvedPauseReason : null,
|
||||||
|
}).catch((err) => {
|
||||||
|
this.logger.warn(`[AGENT-HISTORY] Failed to persist ${historyEventType} for ${agentId}: ${err?.message ?? err}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.logger.log(`[AGENT-HISTORY] ${agentId} action=${action} → no history event (priorState=${priorState ?? 'none'} mapped=${mapped ?? 'null'})`);
|
||||||
|
}
|
||||||
|
|
||||||
if (mapped) {
|
if (mapped) {
|
||||||
this.agentStates.set(agentId, { state: mapped, timestamp: eventTime });
|
this.agentStates.set(agentId, { state: mapped, timestamp: eventTime });
|
||||||
this.agentStateSubject.next({ agentId, state: mapped, timestamp: eventTime });
|
this.agentStateSubject.next({ agentId, state: mapped, timestamp: eventTime });
|
||||||
this.logger.log(`[AGENT-STATE] Emitted: ${agentId} → ${mapped}`);
|
this.logger.log(`[AGENT-STATE] ${agentId} ${priorState ?? 'none'} → ${mapped} (action=${action})`);
|
||||||
|
|
||||||
// Persist to AgentEvent table. CALL_START/CALL_END are
|
|
||||||
// handled in handleCallEvent (they arrive via a separate
|
|
||||||
// Ozonetel webhook). Everything else is captured here.
|
|
||||||
// Pass priorState so 'release' → RESUME / ACW_END / READY can
|
|
||||||
// be disambiguated for the session rollup.
|
|
||||||
const historyEventType = this.mapToHistoryEventType(action, priorState);
|
|
||||||
if (historyEventType) {
|
|
||||||
const resolvedPauseReason = (pauseReason || eventData || '') || null;
|
|
||||||
this.history.persistAgentEvent({
|
|
||||||
ozonetelAgentId: agentId,
|
|
||||||
eventType: historyEventType,
|
|
||||||
eventAt: this.parseOzonetelTime(eventTime),
|
|
||||||
pauseReason: historyEventType === 'PAUSE' ? resolvedPauseReason : null,
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layer 3: ACW auto-dispose safety net
|
// Layer 3: ACW auto-dispose safety net
|
||||||
if (mapped === 'acw') {
|
if (mapped === 'acw') {
|
||||||
|
|||||||
Reference in New Issue
Block a user