fix: SIP driven by Agent entity, token refresh, network indicator

- SIP connection only for users with Agent entity (no env var fallback)
- Supervisor no longer intercepts CC agent calls
- Auth controller checks Agent entity for ALL roles, not just cc-agent
- Token refresh handles GraphQL UNAUTHENTICATED errors (200 with error body)
- Token refresh handles sidecar 400s from expired upstream tokens
- Network quality indicator in sidebar (offline/unstable/good)
- Ozonetel IDLE event mapped to ready state (fixes stuck calling after canceled call)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 11:51:32 +05:30
parent 70e0f6fc3e
commit daa2fbb0c2
4 changed files with 121 additions and 19 deletions

View File

@@ -0,0 +1,46 @@
import { useState, useEffect, useRef } from 'react';
export type NetworkQuality = 'good' | 'unstable' | 'offline';
export const useNetworkStatus = (): NetworkQuality => {
const [quality, setQuality] = useState<NetworkQuality>(navigator.onLine ? 'good' : 'offline');
const dropCountRef = useRef(0);
const resetTimerRef = useRef<number | null>(null);
useEffect(() => {
const handleOffline = () => {
console.log('[NETWORK] Offline');
setQuality('offline');
};
const handleOnline = () => {
console.log('[NETWORK] Back online');
dropCountRef.current++;
// 3+ drops in 2 minutes = unstable
if (dropCountRef.current >= 3) {
setQuality('unstable');
} else {
setQuality('good');
}
// Reset drop counter after 2 minutes of stability
if (resetTimerRef.current) clearTimeout(resetTimerRef.current);
resetTimerRef.current = window.setTimeout(() => {
dropCountRef.current = 0;
if (navigator.onLine) setQuality('good');
}, 120000);
};
window.addEventListener('offline', handleOffline);
window.addEventListener('online', handleOnline);
return () => {
window.removeEventListener('offline', handleOffline);
window.removeEventListener('online', handleOnline);
if (resetTimerRef.current) clearTimeout(resetTimerRef.current);
};
}, []);
return quality;
};