Using WebSockets
Users and clients can connect to an Agent directly over WebSockets, allowing long-running, bi-directional communication with your Agent as it operates.
To enable an Agent to accept WebSockets, define onConnect
and onMessage
methods on your Agent.
onConnect(connection: Connection, ctx: ConnectionContext)
is called when a client establishes a new WebSocket connection. The original HTTP request, including request headers, cookies, and the URL itself, are available onctx.request
.onMessage(connection: Connection, message: WSMessage)
is called for each incoming WebSocket message. Messages are one ofArrayBuffer | ArrayBufferView | string
, and you can send messages back to a client usingconnection.send()
. You can distinguish between client connections by checkingconnection.id
, which is unique for each connected client.
Here's an example of an Agent that echoes back any message it receives:
import { Agent, Connection } from "agents-sdk";
export class ChatAgent extends Agent { async onConnect(connection, ctx) { // Access the request to verify any authentication tokens // provided in headers or cookies let token = ctx.request.headers.get("Authorization"); if (!token) { await connection.close(4000, "Unauthorized"); return; }
// Handle auth using your favorite library and/or auth scheme: // try { // await jwt.verify(token, env.JWT_SECRET); // } catch (error) { // connection.close(4000, 'Invalid Authorization header'); // return; // }
// Accept valid connections connection.accept(); }
async onMessage(connection, message) { // const response = await longRunningAITask(message) await connection.send(message); }}
import { Agent, Connection } from "agents-sdk";
export class ChatAgent extends Agent { async onConnect(connection: Connection, ctx: ConnectionContext) { // Access the request to verify any authentication tokens // provided in headers or cookies let token = ctx.request.headers.get("Authorization"); if (!token) { await connection.close(4000, "Unauthorized"); return; }
// Handle auth using your favorite library and/or auth scheme: // try { // await jwt.verify(token, env.JWT_SECRET); // } catch (error) { // connection.close(4000, 'Invalid Authorization header'); // return; // }
// Accept valid connections connection.accept() }
async onMessage(connection: Connection, message: WSMessage) { // const response = await longRunningAITask(message) await connection.send(message) }}
The Agent framework includes a useful helper package for connecting directly to your Agent (or other Agents) from a client application. Import agents-sdk/client
, create an instance of AgentClient
and use it to connect to an instance of your Agent:
import { AgentClient } from "agents-sdk/client";
const connection = new AgentClient({ agent: "dialogue-agent", name: "insight-seeker",});
connection.addEventListener("message", (event) => { console.log("Received:", event.data);});
connection.send( JSON.stringify({ type: "inquiry", content: "What patterns do you see?", }),);
import { AgentClient } from "agents-sdk/client";
const connection = new AgentClient({ agent: "dialogue-agent", name: "insight-seeker",});
connection.addEventListener("message", (event) => { console.log("Received:", event.data);});
connection.send( JSON.stringify({ type: "inquiry", content: "What patterns do you see?", }));
React-based applications can import agents-sdk/react
and use the useAgent
hook to connect to an instance of an Agent directly:
import { useAgent } from "agents-sdk/react";
function AgentInterface() { const connection = useAgent({ agent: "dialogue-agent", name: "insight-seeker", onMessage: (message) => { console.log("Understanding received:", message.data); }, onOpen: () => console.log("Connection established"), onClose: () => console.log("Connection closed"), });
const inquire = () => { connection.send( JSON.stringify({ type: "inquiry", content: "What insights have you gathered?", }), ); };
return ( <div className="agent-interface"> <button onClick={inquire}>Seek Understanding</button> </div> );}
import { useAgent } from "agents-sdk/react";
function AgentInterface() { const connection = useAgent({ agent: "dialogue-agent", name: "insight-seeker", onMessage: (message) => { console.log("Understanding received:", message.data); }, onOpen: () => console.log("Connection established"), onClose: () => console.log("Connection closed"), });
const inquire = () => { connection.send( JSON.stringify({ type: "inquiry", content: "What insights have you gathered?", }) ); };
return ( <div className="agent-interface"> <button onClick={inquire}>Seek Understanding</button> </div> );}
The useAgent
hook automatically handles the lifecycle of the connection, ensuring that it is properly initialized and cleaned up when the component mounts and unmounts. You can also combine useAgent
with useState
to automatically synchronize state across all clients connected to your Agent.
Define onError
and onClose
methods on your Agent to explicitly handle WebSocket client errors and close events. Log errors, clean up state, and/or emit metrics:
import { Agent, Connection } from "agents-sdk";
export class ChatAgent extends Agent { // onConnect and onMessage methods // ...
// WebSocket error and disconnection (close) handling. async onError(connection, error) { console.error(`WS error: ${error}`); } async onClose(connection, code, reason, wasClean) { console.log(`WS closed: ${code} - ${reason} - wasClean: ${wasClean}`); connection.close(); }}
import { Agent, Connection } from "agents-sdk";
export class ChatAgent extends Agent { // onConnect and onMessage methods // ...
// WebSocket error and disconnection (close) handling. async onError(connection: Connection, error: unknown): Promise<void> { console.error(`WS error: ${error}`); } async onClose(connection: Connection, code: number, reason: string, wasClean: boolean): Promise<void> { console.log(`WS closed: ${code} - ${reason} - wasClean: ${wasClean}`); connection.close(); }}