# Advanced Hooks

Advanced WebRTC callbacks plus low-level WebSocket and SSE client utilities

For most frontend work, `hc()` is enough. This page covers lower-level patterns: advanced `useWebRTCCall()` callbacks for `@agentuity/react` apps, plus the `WebSocketManager` and `EventStreamManager` utilities from `@agentuity/frontend` for custom transports. These managers are also re-exported by `@agentuity/react`, so existing imports still work.

> [!NOTE]
> **Use the Simplest Transport First**
> For Agentuity WebSocket routes, `hc<ApiRouter>()` gives you `.$ws()`. For SSE routes, the standard `EventSource` API usually does the job. Reach for these managers when you want reconnection, message buffering, or a reusable abstraction around a custom endpoint.

In the manager-based examples below, `useEffect` is only for opening and disposing the connection. Keep `send()` and other user-triggered work in event handlers.

## Advanced WebRTC Callbacks

`useWebRTCCall()` accepts the same advanced connection options and callback hooks exposed by the underlying `WebRTCManager`, while still giving you React state for the common case:

```tsx
import { useState } from 'react';
import { useWebRTCCall } from '@agentuity/react';

export function ModeratedRoom({ roomId }: { roomId: string }) {
  const [events, setEvents] = useState<string[]>([]);

  const { state, remotePeerIds, connect, hangup, sendJSON } = useWebRTCCall({
    roomId,
    signalUrl: '/api/call/signal',
    autoConnect: false,
    media: false,
    dataChannels: [{ label: 'presence', ordered: true }],
    callbacks: {
      onStateChange: (from, to) => {
        setEvents((prev) => [`${from} -> ${to}`, ...prev]);
      },
      onPeerJoined: (peerId) => {
        setEvents((prev) => [`peer joined: ${peerId}`, ...prev]);
      },
      onDataChannelMessage: (peerId, label, data) => {
        setEvents((prev) => [`${label} from ${peerId}: ${String(data)}`, ...prev]);
      },
    },
  });

  return (
    <div>
      <p>State: {state}</p>
      <p>Peers: {remotePeerIds.join(', ') || 'none yet'}</p>
      <button onClick={connect}>Join room</button>
      <button onClick={() => sendJSON('presence', { type: 'ping' })}>
        Send presence event
      </button>
      <button onClick={hangup}>Leave</button>
      <pre>{events.join('\n')}</pre>
    </div>
  );
}
```

Use this pattern when you need richer call telemetry, custom data channel flows, or room-specific UX beyond the basic examples on [WebRTC](/routes/webrtc).

## WebSocketManager

Use `WebSocketManager` to manage a custom WebSocket connection with reconnect handling and a typed message callback.

```tsx
import { WebSocketManager } from '@agentuity/frontend';
import { useEffect, useRef, useState } from 'react';

type OutboundMessage = { message: string };
type InboundMessage = { echo: string; timestamp: number };

export function CustomWebSocket() {
  const managerRef = useRef<WebSocketManager<OutboundMessage, InboundMessage> | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const [messages, setMessages] = useState<InboundMessage[]>([]);

  useEffect(() => {
    const url = `${window.location.origin.replace(/^http/, 'ws')}/api/echo`;

    const manager = new WebSocketManager<OutboundMessage, InboundMessage>({
      url,
      callbacks: {
        onConnect: () => setIsConnected(true),
        onDisconnect: () => setIsConnected(false),
        onMessage: (message) => {
          setMessages((prev) => [...prev, message]);
        },
      },
    });

    managerRef.current = manager;
    manager.connect();

    return () => {
      manager.dispose();
    };
  }, []);

  return (
    <div>
      <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
      <button
        onClick={() => managerRef.current?.send({ message: 'ping' })}
        disabled={!isConnected}
      >
        Send Ping
      </button>
      <pre>{JSON.stringify(messages, null, 2)}</pre>
    </div>
  );
}
```

> [!WARNING]
> **Low-Level Managers Do Not Read Provider Context**
> `WebSocketManager` and `EventStreamManager` are transport utilities. They do not automatically read `AgentuityProvider` state. Pass the full URL yourself, and use the auth mechanism your endpoint expects.

**What you get:**

| Capability | Notes |
|-----------|-------|
| `connect()` | Opens the socket and starts reconnect handling |
| `send(data)` | Sends typed outbound messages, or queues them until the socket opens |
| `setMessageHandler(handler)` | Registers a typed message handler and flushes buffered messages |
| `getState()` | Returns `isConnected`, `readyState`, and error state |
| `close()` / `dispose()` | Cleans up the connection and stops reconnects |

## EventStreamManager

Use `EventStreamManager` for long-lived SSE connections when you want the same typed callback and reconnect behavior:

```tsx
import { EventStreamManager } from '@agentuity/frontend';
import { useEffect, useRef, useState } from 'react';

type StatusEvent = { event: string; count: number };

export function CustomEventStream() {
  const managerRef = useRef<EventStreamManager<StatusEvent> | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const [latestEvent, setLatestEvent] = useState<StatusEvent | null>(null);

  useEffect(() => {
    const manager = new EventStreamManager<StatusEvent>({
      url: `${window.location.origin}/api/events`,
      callbacks: {
        onConnect: () => setIsConnected(true),
        onDisconnect: () => setIsConnected(false),
        onMessage: (message) => setLatestEvent(message),
      },
    });

    managerRef.current = manager;
    manager.connect();

    return () => {
      manager.dispose();
    };
  }, []);

  return (
    <div>
      <p>Stream: {isConnected ? 'Active' : 'Connecting...'}</p>
      <pre>{JSON.stringify(latestEvent, null, 2)}</pre>
      <button onClick={() => managerRef.current?.close()}>Stop Stream</button>
    </div>
  );
}
```

## Route-Native Patterns

For Agentuity routes, you often do not need the managers:

```tsx
import { hc } from 'hono/client';
import type { ApiRouter } from '../api/index';

const client = hc<ApiRouter>('/api');

// WebSocket route
const ws = client.echo.$ws();

// SSE route
const events = new EventSource('/api/events');
```

These route-native APIs are the best fit when you already control both ends of the connection.

If you are not in React, import the same low-level managers directly from `@agentuity/frontend`.

## When to Use What

| Use Case | Recommended API |
|----------|------------------|
| Standard HTTP routes | `hc()` or `fetch` |
| Agentuity WebSocket route | `client.route.$ws()` from `hc()` |
| Agentuity SSE route | `EventSource` |
| Custom or third-party WebSocket endpoint | `WebSocketManager` |
| Custom or third-party SSE endpoint | `EventStreamManager` |
| Peer-to-peer audio/video/data | `useWebRTCCall` |

## Next Steps

- [React Hooks](/frontend/react-hooks): Auth, analytics, and WebRTC hooks from `@agentuity/react`
- [RPC Client](/frontend/rpc-client): Type-safe route calls with `hc()`
- [WebSockets](/routes/websockets): Server-side WebSocket routes
- [Server-Sent Events](/routes/sse): Server-side SSE routes