Live agent connection (WebSocket)
Use @coframe/agent-client when you want to show the agent working in real
time — streaming text, tool calls, approvals, and run status — and send
messages back. It connects over a WebSocket with automatic reconnect and a
ready-made React hook.
Installation
Section titled “Installation”pnpm add @coframe/agent-clientQuick start (React) — recommended
Section titled “Quick start (React) — recommended”useAgentChat handles the connection, reconnection, and transcript state for
you. Point it at a session and render the result:
import { useAgentChat } from "@coframe/agent-client/react";
function AgentChat({ sessionId }: { sessionId: string }) { const chat = useAgentChat({ url: `wss://editor.example.com/api/sessions/${sessionId}/socket`, config: { sessionId }, });
return ( <div> <p>Status: {chat.status}</p>
{chat.transcript.map((item, i) => ( <div key={i}>{renderTranscriptItem(item)}</div> ))}
{chat.pendingApproval && ( <button onClick={() => chat.approveTool(chat.pendingApproval!.toolCallId)}> Approve {chat.pendingApproval.toolName} </button> )}
<button onClick={() => chat.sendUserMessage("Make the headline bolder")}> Send </button> </div> );}The hook returns the live status, the available tools, a rolling
transcript, any pendingApproval / pendingElicitation, the last error,
and actions: connect(), sendUserMessage(), approveTool(),
rejectTool(), submitElicitation(), abort(), and disconnect(). It
connects automatically on mount (pass autoConnect: false to opt out).
Quick start (no framework)
Section titled “Quick start (no framework)”Drop down to AgentClient directly when you’re not using React or need finer
control:
import { AgentClient } from "@coframe/agent-client";
const client = new AgentClient({ url: "wss://editor.example.com/api/sessions/my-session/socket", config: { sessionId: "my-session" }, reconnect: { enabled: true, maxAttempts: 5, initialDelayMs: 500 },});
await client.connect();
client.sendUserMessage("Add a testimonials section");
for await (const event of client.events()) { switch (event.type) { case "assistant-delta": process.stdout.write(event.text); break; case "approval-required": client.approveTool(event.toolCallId); break; case "run-finished": console.log("\nrun", event.status); break; }}When reconnect.enabled is true, the events() iterator stays alive
through error → reconnecting → ready cycles, so a dropped connection
doesn’t end your loop.
Events you’ll receive
Section titled “Events you’ll receive”The server streams a stable, app-facing event vocabulary — you render these without needing to know anything about the editor’s internals:
| Event type | Meaning |
|---|---|
run-started | A new agent turn began. |
assistant-delta | Incremental assistant text — concatenate for a live view. |
assistant-message | A finalized assistant message for the turn. |
tool-call | The agent invoked a tool (toolCallId, toolName, args). |
tool-result | A tool finished (ok, result). |
approval-required | A tool needs human approval before running. |
elicitation-required | The agent is waiting for a structured answer (render a form). |
status | A capability lifecycle update (e.g. “starting preview…”). |
run-finished | The turn reached a terminal state (completed / error / aborted / awaiting-input). |
The vocabulary evolves additively — new event types and optional fields may appear without breaking existing clients, so always handle unknown types gracefully.
Commands you can send
Section titled “Commands you can send”| Method | Sends |
|---|---|
sendUserMessage(text) | A user message. |
sendUserContent(blocks) | A user message with structured content blocks. |
approveTool(toolCallId) | Approve a pending tool call. |
rejectTool(toolCallId, reason?) | Reject a pending tool call. |
abort() | Cancel the current run. |
Endpoint & handshake
Section titled “Endpoint & handshake”If you’re implementing the protocol yourself, connect to:
wss://<host>/api/sessions/<sessionId>/socketthen exchange the handshake:
Client → Server: { "type": "open", "protocolVersion": "1", "config": { "sessionId": "..." } }Server → Client: { "type": "ready", "protocolVersion": 1, "sessionId": "...", "tools": [...] }After ready, events arrive wrapped in an envelope —
{ "type": "event", "seq": N, "event": { "type": "...", ... } } — and you
send the commands above as JSON frames.
Live demo
Section titled “Live demo”Connect to a real Coframe Agent backend and watch the protocol in action: