WebSocket
Cognova uses three WebSocket connections for real-time features. All use JSON-encoded messages unless noted otherwise.
WebSocket routes live under server/routes/ in the Nitro server. The chat handler uses /_ws/chat (not /chat) to avoid colliding with the Vue page route. See Architecture for details on the routing convention.
Chat Protocol
URL: ws://<host>/_ws/chat
Streams Claude AI responses in real time. Built on the Claude Agent SDK, supporting multi-turn conversations with tool use. Each conversation is persisted to the database with full message history.
Client Messages
chat:send
Send a message to start or continue a conversation.
{
"type": "chat:send",
"message": "Help me refactor the auth module",
"conversationId": "uuid"
}
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | User message text |
conversationId | string | No | Omit to start a new conversation |
chat:interrupt
Stop the current AI response mid-stream.
{
"type": "chat:interrupt",
"conversationId": "uuid"
}
Server Messages
chat:connected
Sent immediately after the WebSocket connection opens.
{ "type": "chat:connected" }
chat:session_created
Sent when a new conversation is created (no conversationId was provided in chat:send).
{
"type": "chat:session_created",
"conversationId": "uuid"
}
chat:stream_start
Indicates the AI response stream is beginning.
{
"type": "chat:stream_start",
"conversationId": "uuid"
}
chat:text_delta
Incremental text from the AI response. Concatenate deltas to build the full response.
{
"type": "chat:text_delta",
"conversationId": "uuid",
"delta": "Here's how we can "
}
chat:tool_start
The AI is invoking a tool (e.g., file read, bash command).
{
"type": "chat:tool_start",
"conversationId": "uuid",
"toolUseId": "tool-use-id",
"toolName": "Bash"
}
chat:tool_end
A tool invocation completed. The result is truncated to 5000 characters.
{
"type": "chat:tool_end",
"conversationId": "uuid",
"toolUseId": "tool-use-id",
"result": "Command output...",
"isError": false
}
chat:stream_end
The AI response is complete. Includes cost and timing metadata.
{
"type": "chat:stream_end",
"conversationId": "uuid",
"costUsd": 0.035,
"durationMs": 8200
}
chat:interrupted
Sent after a successful chat:interrupt request.
{
"type": "chat:interrupted",
"conversationId": "uuid"
}
chat:error
An error occurred during processing.
{
"type": "chat:error",
"conversationId": "uuid",
"message": "Conversation not found"
}
Connection Lifecycle
- Client opens WebSocket to
/_ws/chat - Server sends
chat:connected - Client sends
chat:send(withoutconversationIdfor a new conversation) - Server sends
chat:session_createdwith the newconversationId - Server sends
chat:stream_start - Server streams
chat:text_deltamessages as the AI generates text - If tools are used:
chat:tool_startfollowed bychat:tool_endfor each tool call - Server sends
chat:stream_endwhen the response is complete - Client can send additional
chat:sendmessages with the sameconversationIdto continue
Example Message Flow
Client Server
| |
|--- connect ------------------->|
|<-- chat:connected ------------|
| |
|--- chat:send (new) ---------->|
|<-- chat:session_created ------|
|<-- chat:stream_start ---------|
|<-- chat:text_delta -----------|
|<-- chat:text_delta -----------|
|<-- chat:tool_start -----------|
|<-- chat:tool_end -------------|
|<-- chat:text_delta -----------|
|<-- chat:stream_end ------------|
| |
|--- chat:send (continue) ----->|
|<-- chat:stream_start ---------|
|<-- chat:text_delta -----------|
|--- chat:interrupt ----------->|
|<-- chat:interrupted ----------|
Terminal Protocol
URL: ws://<host>/terminal
Provides a full PTY (pseudo-terminal) session over WebSocket using node-pty. The terminal session persists across disconnections -- reconnecting clients receive a replay of the output buffer.
Client Messages
input
Send keystrokes to the terminal.
{
"type": "input",
"data": "ls -la\r"
}
resize
Resize the terminal dimensions.
{
"type": "resize",
"cols": 120,
"rows": 40
}
ping
Keepalive ping.
{ "type": "ping" }
Server Messages
output
Terminal output data. On reconnection, this may contain the full buffered output history.
{
"type": "output",
"data": "total 48\ndrwxr-xr-x 12 user staff 384 Feb 18 10:00 .\n"
}
pong
Response to a ping message.
{ "type": "pong" }
error
Terminal initialization failure.
{
"type": "error",
"data": "Failed to start terminal: spawn /bin/bash ENOENT\r\n"
}
Connection Lifecycle
- Client opens WebSocket to
/terminal - Server creates a new PTY session (or reconnects to existing one)
- If reconnecting, server replays the output buffer as a single
outputmessage - Server forwards all PTY output as
outputmessages in real time - Client sends
inputmessages with raw keystrokes (including\rfor Enter) - On disconnect, the PTY session is preserved for reconnection (cleaned up by timeout)
The terminal defaults to 80 columns by 24 rows. Send a resize message immediately after connection to match the client viewport. Use \r (carriage return) in input.data to simulate pressing Enter.
Notifications Protocol
URL: ws://<host>/notifications
A server-push channel for real-time UI updates. The server broadcasts resource_change events whenever resources are created, updated, or deleted through the API.
Client Messages
ping
Keepalive ping.
{ "type": "ping" }
Server Messages
connected
Sent immediately after the WebSocket connection opens.
{
"type": "connected",
"message": "Notification bus connected",
"timestamp": "2026-02-18T12:00:00.000Z"
}
pong
Response to a ping message.
{
"type": "pong",
"timestamp": "2026-02-18T12:00:00.000Z"
}
resource_change
Broadcast when any resource is modified through the API.
{
"type": "resource_change",
"resource": "task",
"action": "create",
"resourceId": "uuid",
"resourceName": "Fix login bug",
"title": "Task created",
"message": "Task \"Fix login bug\" was created",
"color": "success",
"timestamp": "2026-02-18T12:00:00.000Z"
}
Resource types: task, reminder, agent, hook, memory, document, project, conversation, secret
Action types: create, edit, delete, restore, run, cancel, complete, fail
Color mapping:
| Action | Color |
|---|---|
create, restore, complete | success |
edit, run | info |
delete, cancel | warning |
fail | error |
Connection Lifecycle
- Client opens WebSocket to
/notifications - Server registers the peer and sends
connected - Server broadcasts
resource_changeevents as they occur - On disconnect, the server automatically unregisters the peer
- On error, the peer is unregistered to prevent stale connections
The notification bus is a singleton that broadcasts to all connected peers. There is no subscription filtering -- clients receive all resource change events and should filter client-side based on user notification preferences. See Settings for notification preference configuration.