feat: dynamic SIP from agentConfig, logout cleanup, heartbeat

- SIP provider reads credentials from agentConfig (login response)
- Auth logout calls sidecar to unlock Redis + Ozonetel logout
- AppShell heartbeat every 5 min for CC agents
- Login stores agentConfig in localStorage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 21:24:47 +05:30
parent b9b7ee275f
commit 3afa4f20b2
4 changed files with 61 additions and 9 deletions

View File

@@ -13,12 +13,27 @@ import {
import { registerSipStateUpdater, connectSip, disconnectSip, getSipClient } from '@/state/sip-manager';
import type { SIPConfig } from '@/types/sip';
const DEFAULT_CONFIG: SIPConfig = {
displayName: import.meta.env.VITE_SIP_DISPLAY_NAME ?? 'Helix Agent',
uri: import.meta.env.VITE_SIP_URI ?? '',
password: import.meta.env.VITE_SIP_PASSWORD ?? '',
wsServer: import.meta.env.VITE_SIP_WS_SERVER ?? '',
stunServers: 'stun:stun.l.google.com:19302',
const getSipConfig = (): SIPConfig => {
try {
const stored = localStorage.getItem('helix_agent_config');
if (stored) {
const config = JSON.parse(stored);
return {
displayName: 'Helix Agent',
uri: config.sipUri,
password: config.sipPassword,
wsServer: config.sipWsServer,
stunServers: 'stun:stun.l.google.com:19302',
};
}
} catch {}
return {
displayName: import.meta.env.VITE_SIP_DISPLAY_NAME ?? 'Helix Agent',
uri: import.meta.env.VITE_SIP_URI ?? '',
password: import.meta.env.VITE_SIP_PASSWORD ?? '',
wsServer: import.meta.env.VITE_SIP_WS_SERVER ?? '',
stunServers: 'stun:stun.l.google.com:19302',
};
};
export const SipProvider = ({ children }: PropsWithChildren) => {
@@ -41,7 +56,7 @@ export const SipProvider = ({ children }: PropsWithChildren) => {
// Auto-connect SIP on mount
useEffect(() => {
connectSip(DEFAULT_CONFIG);
connectSip(getSipConfig());
}, []);
// Call duration timer
@@ -129,7 +144,7 @@ export const useSip = () => {
isInCall: ['ringing-in', 'ringing-out', 'active'].includes(callState),
ozonetelStatus: 'logged-in' as const,
ozonetelError: null as string | null,
connect: () => connectSip(DEFAULT_CONFIG),
connect: () => connectSip(getSipConfig()),
disconnect: disconnectSip,
makeCall,
answer,