Skip to main content

Socket.IO Gateway

createAgentGateway() attaches real-time handlers to a Socket.IO server. Clients emit agent.run or team.run and receive streaming chunks, tool events, and final output over WebSockets.

Installation

npm install @radaros/transport socket.io

Basic Setup

Explicit wiring

import { createServer } from "http";
import { Server } from "socket.io";
import { createAgentGateway } from "@radaros/transport";
import { Agent, openai } from "@radaros/core";

const agent = new Agent({
  name: "Assistant",
  model: openai("gpt-4o"),
  instructions: "You are helpful.",
});

const httpServer = createServer();
const io = new Server(httpServer);

createAgentGateway({
  io,
  agents: { assistant: agent },
  namespace: "/radaros", // default
});

httpServer.listen(3000);

Auto-discovery (zero-wiring)

Agents and teams auto-register into a global registry. The gateway reads from it dynamically — entities created after the gateway starts are immediately reachable.
import { createServer } from "http";
import { Server } from "socket.io";
import { createAgentGateway } from "@radaros/transport";
import { Agent, openai } from "@radaros/core";

const httpServer = createServer();
const io = new Server(httpServer);

createAgentGateway({ io }); // no agents/teams needed

// Created later — immediately available via Socket.IO
new Agent({ name: "assistant", model: openai("gpt-4o") });

httpServer.listen(3000);
You can also pass a mixed array via serve:
createAgentGateway({
  io,
  serve: [assistant, analyst, researchTeam],
});

GatewayOptions

io
Server
required
Socket.IO server instance.
agents
Record<string, Agent>
Map of agent names to Agent instances.
teams
Record<string, Team>
Map of team names to Team instances.
serve
Servable[]
Mixed array of Agent and Team instances. Automatically classified. An alternative to passing agents and teams separately.
registry
Registry | false
Controls live auto-discovery. Defaults to the global registry — all auto-registered entities are available. Pass a custom Registry instance, or false to disable auto-discovery and only serve explicitly passed entities.
namespace
string
default:"/radaros"
Socket.IO namespace. Clients connect to http://host/radaros (or your namespace).
authMiddleware
(socket, next) => void
Socket.IO middleware for authentication. Call next() to allow, or next(new Error("Unauthorized")) to reject.
toolkits
Toolkit[]
Toolkit instances whose tools are exposed via tools.list event. Useful for UI tool discovery.
toolLibrary
Record<string, ToolDef>
Named tools exposed via tools.list. Merged with toolkit tools (explicit entries take precedence).

Events

Client → Server

EventPayloadDescription
agent.run{ name, input, sessionId?, apiKey? }Run agent and stream response
team.run{ name, input, sessionId?, apiKey? }Run team (non-streaming)
agents.list{}List registered agents with metadata (callback)
teams.list{}List registered teams (callback)
workflows.list{}List registered workflows (callback)
registry.list{}List all registered entity names (callback)
tools.list{}List available tools (when toolkits configured)
tools.get{ name }Get single tool detail

Server → Client

EventPayloadDescription
agent.chunk{ chunk: string }Streamed text chunk
agent.tool.call{ toolName, args }Tool call started
agent.tool.done{ toolCallId }Tool call finished
agent.done{ output: { text } }Run complete
agent.error{ error: string }Error occurred

Listing Agents & Teams

Use acknowledgement callbacks to query registered entities:
socket.emit("agents.list", {}, (agents) => {
  console.log(agents);
  // [{ name: "assistant", model: "gpt-4o", provider: "openai", tools: ["search"], hasStructuredOutput: false }]
});

socket.emit("teams.list", {}, (teams) => {
  console.log(teams);
  // [{ name: "research" }]
});

socket.emit("registry.list", {}, (data) => {
  console.log(data);
  // { agents: ["assistant"], teams: ["research"], workflows: [] }
});

Real-Time Chat Example

Server:
import { createServer } from "http";
import { Server } from "socket.io";
import { createAgentGateway } from "@radaros/transport";
import { Agent, openai } from "@radaros/core";

const agent = new Agent({
  name: "ChatBot",
  model: openai("gpt-4o"),
  instructions: "You are a friendly chat assistant.",
});

const httpServer = createServer();
const io = new Server(httpServer, {
  cors: { origin: "*" },
});

createAgentGateway({
  io,
  agents: { chatbot: agent },
  namespace: "/chat",
});

httpServer.listen(3000);
Client (browser):
<script src="https://cdn.socket.io/4.8.3/socket.io.min.js"></script>
<script>
  const socket = io("http://localhost:3000/chat");

  socket.on("agent.chunk", ({ chunk }) => {
    document.getElementById("output").textContent += chunk;
  });

  socket.on("agent.done", ({ output }) => {
    console.log("Done:", output.text);
  });

  socket.on("agent.error", ({ error }) => {
    console.error("Error:", error);
  });

  socket.emit("agent.run", {
    name: "chatbot",
    input: "Tell me a joke.",
    sessionId: "user-123",
  });
</script>

With Authentication

createAgentGateway({
  io,
  agents: { assistant: agent },
  authMiddleware: (socket, next) => {
    const token = socket.handshake.auth?.token;
    if (!token || !validateToken(token)) {
      return next(new Error("Unauthorized"));
    }
    socket.userId = decodeToken(token).userId;
    next();
  },
});
Clients can pass apiKey in the event payload or in handshake.auth:
const socket = io("http://localhost:3000/radaros", {
  auth: { apiKey: "sk-..." },
});

socket.emit("agent.run", {
  name: "assistant",
  input: "Hello",
  apiKey: "sk-...", // or use handshake.auth.apiKey
});

Session Continuity

Use sessionId to maintain conversation context across multiple agent.run calls:
const sessionId = `user-${userId}-${Date.now()}`;

socket.emit("agent.run", {
  name: "assistant",
  input: "My name is Alice.",
  sessionId,
});

// Later, same session
socket.emit("agent.run", {
  name: "assistant",
  input: "What's my name?",
  sessionId,
});

Voice Gateway

For real-time voice over Socket.IO, see the dedicated Voice Agents docs. The createVoiceGateway() function streams audio between browser clients and VoiceAgent instances using the same Socket.IO server.
import { VoiceAgent, openaiRealtime } from "@radaros/core";
import { createVoiceGateway } from "@radaros/transport";

const agent = new VoiceAgent({
  name: "assistant",
  provider: openaiRealtime("gpt-4o-realtime-preview"),
  instructions: "You are a voice assistant.",
  voice: "alloy",
});

createVoiceGateway({
  agents: { assistant: agent },
  io,
  namespace: "/voice",
});

Browser Gateway

For live browser agent observation over Socket.IO, see the Browser Agents docs. The createBrowserGateway() function streams screenshots, actions, and step events from BrowserAgent runs to connected clients.
import { BrowserAgent } from "@radaros/browser";
import { createBrowserGateway } from "@radaros/transport";

createBrowserGateway({
  agents: { browser: agent },
  io,
  namespace: "/browser",
  streamScreenshots: true,
});