fix: persist LOGIN events for session rollup — fixes zero dashboard metrics

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:
2026-04-20 10:27:40 +05:30
parent 7402590969
commit 350fcdd926

View File

@@ -176,27 +176,31 @@ 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] Emitted: ${agentId}${mapped}`);
// 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') {
// Find the most recent UCID for this agent // Find the most recent UCID for this agent