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

@@ -88,6 +88,21 @@ const handleResponse = async <T>(response: Response, silent = false, retryFn?: (
const json = await response.json().catch(() => null);
// Sidecar may return 400 when the underlying platform token expired — retry with refreshed token
if (!response.ok && retryFn) {
const msg = (json?.message ?? '').toLowerCase();
if (msg.includes('agent identity') || msg.includes('token') || msg.includes('unauthenticated')) {
const refreshed = await tryRefreshToken();
if (refreshed) {
const retryResponse = await retryFn();
return handleResponse<T>(retryResponse, silent);
}
clearTokens();
if (!silent) notify.error('Session expired. Please log in again.');
throw new AuthError();
}
}
if (!response.ok) {
const message = json?.message ?? json?.error ?? `Request failed (${response.status})`;
if (!silent) notify.error(message);
@@ -152,7 +167,22 @@ export const apiClient = {
}
}
const json = await response.json();
let json = await response.json();
// Platform returns 200 with UNAUTHENTICATED error when token expires — retry with refresh
const authError = json.errors?.find((e: any) => e.extensions?.code === 'UNAUTHENTICATED');
if (authError) {
const refreshed = await tryRefreshToken();
if (refreshed) {
const retryResponse = await doFetch();
json = await retryResponse.json();
} else {
clearTokens();
if (!options?.silent) notify.error('Session expired', 'Please log in again.');
throw new AuthError();
}
}
if (json.errors) {
const message = json.errors[0]?.message ?? 'GraphQL error';
if (!options?.silent) notify.error('Query failed', message);