> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Socket.IO Gateway

> createAgentGateway for real-time agent streaming. Events, namespace, auth, and a chat example.

# 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

<CodeGroup>
  ```bash npm theme={null}
  npm install @agentium/transport socket.io
  ```

  ```bash pnpm theme={null}
  pnpm add @agentium/transport socket.io
  ```
</CodeGroup>

***

## Basic Setup

### Explicit wiring

```typescript theme={null}
import { createServer } from "http";
import { Server } from "socket.io";
import { createAgentGateway } from "@agentium/transport";
import { Agent, openai } from "@agentium/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: "/agentium", // 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.

```typescript theme={null}
import { createServer } from "http";
import { Server } from "socket.io";
import { createAgentGateway } from "@agentium/transport";
import { Agent, openai } from "@agentium/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`:

```typescript theme={null}
createAgentGateway({
  io,
  serve: [assistant, analyst, researchTeam],
});
```

***

## GatewayOptions

<ParamField path="io" type="Server" required>
  Socket.IO server instance.
</ParamField>

<ParamField path="agents" type="Record<string, Agent>" required={false}>
  Map of agent names to Agent instances.
</ParamField>

<ParamField path="teams" type="Record<string, Team>" required={false}>
  Map of team names to Team instances.
</ParamField>

<ParamField path="serve" type="Servable[]" required={false}>
  Mixed array of Agent and Team instances. Automatically classified. An alternative to passing `agents` and `teams` separately.
</ParamField>

<ParamField path="registry" type="Registry | false" required={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.
</ParamField>

<ParamField path="namespace" type="string" required={false} default="/agentium">
  Socket.IO namespace. Clients connect to `http://host/agentium` (or your namespace).
</ParamField>

<ParamField path="authMiddleware" type="(socket, next) => void" required={false}>
  Socket.IO middleware for authentication. Call `next()` to allow, or `next(new Error("Unauthorized"))` to reject.
</ParamField>

<ParamField path="toolkits" type="Toolkit[]" required={false}>
  Toolkit instances whose tools are exposed via `tools.list` event. Useful for UI tool discovery.
</ParamField>

<ParamField path="toolLibrary" type="Record<string, ToolDef>" required={false}>
  Named tools exposed via `tools.list`. Merged with toolkit tools (explicit entries take precedence).
</ParamField>

***

## Events

### Client → Server

| Event            | Payload                                | Description                                     |
| ---------------- | -------------------------------------- | ----------------------------------------------- |
| `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

| Event             | Payload                        | Description                                                         |
| ----------------- | ------------------------------ | ------------------------------------------------------------------- |
| `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, usage? } }` | Run complete — `usage` includes `providerMetrics` with raw API data |
| `agent.error`     | `{ error: string }`            | Error occurred                                                      |

***

## Listing Agents & Teams

Use acknowledgement callbacks to query registered entities:

```javascript theme={null}
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:**

```typescript theme={null}
import { createServer } from "http";
import { Server } from "socket.io";
import { createAgentGateway } from "@agentium/transport";
import { Agent, openai } from "@agentium/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):**

```html theme={null}
<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

```typescript theme={null}
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`:

```javascript theme={null}
const socket = io("http://localhost:3000/agentium", {
  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:

```javascript theme={null}
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](/voice/overview) docs. The `createVoiceGateway()` function streams audio between browser clients and `VoiceAgent` instances using the same Socket.IO server.

```typescript theme={null}
import { VoiceAgent, openaiRealtime } from "@agentium/core";
import { createVoiceGateway } from "@agentium/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",
});
```

## Voice Gateway Security

The voice gateway validates all incoming data to prevent abuse:

* **Audio data** (`voice.audio`): Must be a string with a maximum size limit. `Buffer.from()` decoding is wrapped in `try/catch` to handle malformed base64 data.
* **Text data** (`voice.text`): Must be a string with a maximum length limit.
* **Session cleanup**: `session.close()` errors in `voice.stop` are caught and logged instead of crashing the server.

***

## Browser Gateway

For **live browser agent observation** over Socket.IO, see the [Browser Agents](/browser/overview) docs. The `createBrowserGateway()` function streams screenshots, actions, and step events from `BrowserAgent` runs to connected clients.

```typescript theme={null}
import { BrowserAgent } from "@agentium/browser";
import { createBrowserGateway } from "@agentium/transport";

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