Build/Routes

Using WebSockets

Real-time bidirectional communication with router.websocket()

WebSockets enable persistent, bidirectional connections between client and server. Use them for chat interfaces, live dashboards, collaborative tools, and any scenario requiring real-time two-way communication.

Routes Location

All routes live in src/api/. Import agents you need and call them directly.

Basic Example

import { createRouter } from '@agentuity/runtime';
import chatAgent from '@agent/chat';
 
const router = createRouter();
 
router.websocket('/chat', (c) => (ws) => {
  ws.onOpen(() => {
    c.var.logger.info('Client connected');
    ws.send('Connected!');
  });
 
  ws.onMessage(async (event) => {
    const message = event.data as string;
    const response = await chatAgent.run({ message });
    ws.send(response);
  });
 
  ws.onClose(() => {
    c.var.logger.info('Client disconnected');
  });
});
 
export default router;

Handler Structure

The WebSocket handler uses a callback pattern:

router.websocket('/path', (c) => (ws) => {
  // c - Hono context (available in closure)
  // ws - WebSocket connection object
 
  ws.onOpen(() => { /* connection opened */ });
  ws.onMessage(async (event) => { /* message received */ });
  ws.onClose(() => { /* connection closed */ });
});

WebSocket Events

EventTriggerExample Use Case
onOpenConnection establishedSend welcome message, initialize state
onMessageClient sends dataProcess messages, call agents
onCloseConnection endsClean up resources

With Middleware

Apply authentication or logging before the WebSocket upgrade:

import { createRouter } from '@agentuity/runtime';
import { createMiddleware } from 'hono/factory';
 
const router = createRouter();
 
const authMiddleware = createMiddleware(async (c, next) => {
  const token = c.req.query('token');
  if (!token) {
    return c.text('Unauthorized', 401);
  }
  c.set('userId', await validateToken(token));
  await next();
});
 
import chat from '@agent/chat';
 
router.websocket('/chat', authMiddleware, (c) => (ws) => {
  const userId = c.var.userId;
 
  ws.onOpen(() => {
    ws.send(`Welcome, user ${userId}!`);
  });
 
  ws.onMessage(async (event) => {
    const response = await chat.run({
      userId,
      message: event.data as string,
    });
    ws.send(response);
  });
});
 
export default router;

Server Push

Send data to the client without waiting for a request:

router.websocket('/notifications', (c) => (ws) => {
  let heartbeat: Timer;
 
  ws.onOpen(() => {
    ws.send(JSON.stringify({ type: 'connected' }));
 
    // Push updates every 5 seconds
    heartbeat = setInterval(() => {
      ws.send(JSON.stringify({
        type: 'heartbeat',
        time: new Date().toISOString(),
      }));
    }, 5000);
  });
 
  ws.onClose(() => {
    clearInterval(heartbeat); // Clean up!
  });
});

Full Example

A real-time echo server with heartbeat:

import { createRouter } from '@agentuity/runtime';
import echoAgent from '@agent/echo';
 
const router = createRouter();
 
router.websocket('/', (c) => (ws) => {
  let heartbeat: Timer;
 
  ws.onOpen(() => {
    c.var.logger.info('WebSocket connected');
    ws.send('Connected! Send a message to echo it back.');
 
    heartbeat = setInterval(() => {
      ws.send(`Ping: ${new Date().toLocaleTimeString()}`);
    }, 5000);
  });
 
  ws.onMessage(async (event) => {
    try {
      const message = event.data as string;
      c.var.logger.info('Message received', { message });
 
      const response = await echoAgent.run(message);
      ws.send(response);
    } catch (error) {
      c.var.logger.error('Message processing failed', { error });
      ws.send(JSON.stringify({ error: 'Processing failed' }));
    }
  });
 
  ws.onClose(() => {
    c.var.logger.info('WebSocket disconnected');
    clearInterval(heartbeat);
  });
});
 
export default router;

Resource Cleanup

Always clean up intervals, subscriptions, or other resources in onClose:

ws.onClose(() => {
  clearInterval(heartbeat);  // Prevent memory leaks
  subscription.unsubscribe();
});

Failing to clean up can cause memory leaks and unexpected behavior.

Client Connection

Connect from a browser or any WebSocket client:

const ws = new WebSocket('wss://your-project.agentuity.cloud/agent-name');
 
ws.onopen = () => {
  console.log('Connected');
  ws.send('Hello!');
};
 
ws.onmessage = (event) => {
  console.log('Received:', event.data);
};
 
ws.onclose = () => {
  console.log('Disconnected');
};

When to Use WebSockets

Use CaseWebSocketSSEHTTP
Chat / messaging
Live collaboration
Real-time dashboards
Progress updates
Request/response API

Use WebSockets when you need bidirectional communication. For server-to-client only streaming, consider Server-Sent Events.

Standalone Usage

WebSocket handlers work without agents. This example broadcasts system metrics to connected clients:

import { createRouter } from '@agentuity/runtime';
 
const router = createRouter();
 
router.websocket('/metrics', (c) => (ws) => {
  const interval = setInterval(() => {
    ws.send(JSON.stringify({
      cpu: Math.random() * 100,
      memory: Math.random() * 100,
      timestamp: Date.now(),
    }));
  }, 5000);
 
  ws.onClose(() => clearInterval(interval));
});
 
export default router;

Next Steps

Need Help?

Join our DiscordCommunity for assistance or just to hang with other humans building agents.

Send us an email at hi@agentuity.com if you'd like to get in touch.

Please Follow us on

If you haven't already, please Signup for your free account now and start building your first agent!