WebSocket API for agent debugging
Agent Daddy is a real-time debugging mirror. Your agent connects via WebSocket, fires events (fire-and-forget), and they show up instantly in the dashboard chat UI.
Server
https://daddy.codeaxolot.com
WebSocket (wss://) is handled automatically by Socket.IO when connecting over https.
Connect via WebSocket with your agent_id and role=agent. All communication happens over WebSocket (wss://) — no HTTP API, no polling.
Python (pip install python-socketio[client] websocket-client)
import socketio sio = socketio.Client() sio.connect( "https://daddy.codeaxolot.com", transports=["websocket"], socketio_path="socket.io", headers={"User-Agent": "my-agent"}, auth={ "role": "agent", "agent_id": "my-agent" } ) # Note: python-socketio passes auth differently from query params. # You can also use query string style: sio.connect( "https://daddy.codeaxolot.com?role=agent&agent_id=my-agent", transports=["websocket"] )
JavaScript / Node.js (npm install socket.io-client)
import { io } from "socket.io-client"; const socket = io("https://daddy.codeaxolot.com", { query: { role: "agent", agent_id: "my-agent" }, transports: ["websocket"] });
Emit an "event" over the WebSocket. Fire-and-forget — no response, no acknowledgement needed.
| Field | Type | Required | Description |
|---|---|---|---|
chat_id |
string | yes | Identifies which conversation/chat this belongs to. Each chat gets its own color pill in the dashboard and can be filtered. |
direction |
string | yes | "sent" = agent/subprocess action (shown RIGHT), "received" = user/external input (shown LEFT) |
content |
any | yes | Message content — string or JSON object |
kind |
string | no | Visual shape: "message" (default bubble), "notification", "error", "context" |
user |
object | no | Sender info: { name?, username?, avatar? }. Shows avatar + name on the bubble. |
metadata |
object | no | Extra context — type, model, tokens, etc. |
kind — visual shapes
Chat bubble — left or right based on direction. Use for conversation messages, tool calls, tool results.
Centered banner — for status updates, background process starts, info the agent wants to surface.
Red alert banner — for errors, failures, exceptions. Stands out immediately.
Compact summary bubble with "Explore" button. Opens a Context Explorer panel on the left to browse messages and attachments. Server computes stats (message count, total size).
Layout: direction: "sent" = agent & subprocess messages appear on the right.
direction: "received" = user & external messages appear on the left.
Each chat_id gets a unique color pill so you can visually distinguish conversations.
Note: agent_id is set once at connection time via query params, not per event.
Recommended metadata.type values
Python — log a conversation turn
# User sends a message to the agent (left side) sio.emit("event", { "chat_id": "chat-abc-123", "direction": "received", "content": "What is the weather in Tokyo?", "metadata": { "type": "user_message" } }) # Agent responds (right side) sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "content": "The weather in Tokyo is 15°C and cloudy.", "metadata": { "type": "assistant_message", "model": "claude-sonnet-4-6", "tokens": 42 } })
Python — log a tool call + result
# Agent/subprocess calls a tool (right side) sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "content": { "tool": "web_search", "input": { "query": "Tokyo weather" } }, "metadata": { "type": "tool_call" } }) # Tool result comes back (right side — still agent's subprocess) sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "content": { "tool": "web_search", "result": { "temp": "15°C", "condition": "cloudy" } }, "metadata": { "type": "tool_result", "duration_ms": 1200 } })
JavaScript — same thing
// User message (left side) socket.emit("event", { chat_id: "chat-abc-123", direction: "received", content: "What is the weather in Tokyo?", metadata: { type: "user_message" } }); // Agent response (right side) socket.emit("event", { chat_id: "chat-abc-123", direction: "sent", content: "15°C and cloudy.", metadata: { type: "assistant_message" } });
Python — notifications, errors, context
# Notification — centered banner, not a bubble sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "kind": "notification", "content": "Starting background research on Tokyo weather patterns..." }) # Error — red alert banner sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "kind": "error", "content": "API rate limit exceeded. Retrying in 30s.", "metadata": { "status": 429, "retry_after": 30 } }) # Agent context — full conversation with model info sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "kind": "context", "content": { "context_type": "agent", "model": "claude-sonnet-4-6", "messages": [ { "role": "system", "content": "You are a helpful weather assistant..." }, { "role": "user", "content": "What is the weather in Tokyo?" }, { "role": "assistant", "content": "Let me check..." } ] } }) # Tool call context — sub-model invocation sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "kind": "context", "content": { "context_type": "tool_call", "model": "claude-haiku-4-5-20251001", "messages": [ { "role": "user", "content": "Summarize this", "attachments": [ { "name": "doc.pdf", "type": "application/pdf", "size": 245000 } ]}, { "role": "assistant", "content": "Here is the summary..." } ] } })
Attach a user object to any event to show name, username, and avatar on the bubble.
| user field | Type | Description |
|---|---|---|
name |
string | Display name shown on the bubble header |
username |
string | Handle shown as @username next to name |
avatar |
string | URL to avatar image — shown as a circle next to the bubble |
# User message with user info sio.emit("event", { "chat_id": "chat-abc-123", "direction": "received", "content": "Can you check the weather?", "user": { "name": "John Doe", "username": "johnd", "avatar": "https://i.pravatar.cc/150?u=john" } }) # Agent reply with its own identity sio.emit("event", { "chat_id": "chat-abc-123", "direction": "sent", "content": "Sure! Checking now...", "user": { "name": "Weather Bot", "avatar": "https://i.pravatar.cc/150?u=bot" } })
If no user is provided, the bubble shows the agent_id with initials as the avatar. All three fields are optional — send any combination.
Send full conversation contexts using kind: "context". The content should have a messages array. The server computes stats automatically. In the dashboard, context appears as a compact summary bubble with an "Explore" button that opens the Context Explorer panel on the left.
Context content structure
| Field | Type | Description |
|---|---|---|
content.context_type |
string | Type badge: "agent", "tool_call", "system", "memory" |
content.model |
string | Model name shown as pill on bubble (e.g. "claude-sonnet-4-6") |
content.messages |
array | Array of message objects (see below) |
Message object fields
| Field | Type | Description |
|---|---|---|
role |
string | "user", "assistant", "system", "tool" |
content |
string | object | Message text or structured content |
name |
string | Optional — tool name for role "tool" |
attachments |
array | Optional — array of { name, type, size } (metadata only, don't send file data) |
Server-computed stats (context_stats)
message_count — number of messages in the contexttext_bytes — total bytes of message contentattachment_bytes — total bytes of all attachments (from size fields)attachment_count — number of attachmentstotal_bytes — text_bytes + attachment_bytescontext_type — visual badges
Agent's own conversation context — the full chat history the agent is working with.
Context for a tool call or sub-agent invocation — what was sent to a sub-model.
System prompt / configuration context.
Memory or retrieval context — RAG results, recalled info.
Context Explorer: When you click "Explore" on a context bubble, the left panel opens showing all messages row-by-row with role badges, content previews, and attachment metadata. The source bubble gets highlighted in the chat. Only one context can be explored at a time. Context type and model are shown in both the bubble and the explorer header.
Emit "batch" with an array of events. Same fields, just wrapped in an array.
sio.emit("batch", [ { "chat_id": "chat-abc-123", "direction": "received", "content": "Hello" }, { "chat_id": "chat-abc-123", "direction": "sent", "content": "Hi there!" } ])
Emit "register_tools" with an array of tool definitions. They appear in the tools panel on the right side of the dashboard.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Tool name |
description |
string | no | What the tool does |
parameters |
object | no | Map of param name to { type, description, required } |
returns |
string | no | Return type description |
sio.emit("register_tools", [ { "name": "web_search", "description": "Search the web for information", "parameters": { "query": { "type": "string", "description": "Search query", "required": True }, "limit": { "type": "number", "description": "Max results to return", "required": False } }, "returns": "array of { title, url, snippet }" }, { "name": "read_file", "description": "Read contents of a file", "parameters": { "path": { "type": "string", "description": "File path to read", "required": True } }, "returns": "string" } ])
Python (pip install python-socketio[client] websocket-client)
import socketio sio = socketio.Client() sio.connect( "https://daddy.codeaxolot.com?role=agent&agent_id=my-agent", transports=["websocket"] ) # Register your tools on connect sio.emit("register_tools", [ { "name": "web_search", "description": "Search the web", "parameters": { "query": { "type": "string", "required": True } }, "returns": "array of results" } ]) # In your agent loop — user message (left) then agent reply (right): sio.emit("event", { "chat_id": "session-001", "direction": "received", "content": "What is the weather?", "metadata": { "type": "user_message" } }) sio.emit("event", { "chat_id": "session-001", "direction": "sent", "content": "It's 15°C in Tokyo.", "metadata": { "type": "assistant_message", "tokens": 12 } }) # When done sio.disconnect()
JavaScript / Node.js (npm install socket.io-client)
import { io } from "socket.io-client"; const socket = io("https://daddy.codeaxolot.com", { query: { role: "agent", agent_id: "my-agent" }, transports: ["websocket"] }); socket.on("connect", () => { // Register tools socket.emit("register_tools", [ { name: "web_search", description: "Search the web", parameters: { query: { type: "string", required: true } }, returns: "array of results" } ]); // Log events — user input on left, agent output on right socket.emit("event", { chat_id: "session-001", direction: "received", content: "What is the weather?", metadata: { type: "user_message" } }); socket.emit("event", { chat_id: "session-001", direction: "sent", content: "It's 15°C in Tokyo.", metadata: { type: "assistant_message" } }); });
| WebSocket Event | Direction | Payload |
|---|---|---|
"event" |
agent → server | { chat_id, direction, content, kind?, user?, metadata? } |
"batch" |
agent → server | [ { chat_id, direction, content, kind?, user?, metadata? }, ... ] |
"register_tools" |
agent → server | [ { name, description?, parameters?, returns? }, ... ] |
Protocol
role=agent and agent_id=your-name as query paramsdirection: "sent" = agent & subprocesses (RIGHT), direction: "received" = user & external (LEFT)chat_id gets a unique color pill — use it to separate conversations