API Overview

WebSocket

Real-time protocols for chat streaming, terminal PTY, and push notifications.

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"
}
FieldTypeRequiredDescription
messagestringYesUser message text
conversationIdstringNoOmit 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

  1. Client opens WebSocket to /_ws/chat
  2. Server sends chat:connected
  3. Client sends chat:send (without conversationId for a new conversation)
  4. Server sends chat:session_created with the new conversationId
  5. Server sends chat:stream_start
  6. Server streams chat:text_delta messages as the AI generates text
  7. If tools are used: chat:tool_start followed by chat:tool_end for each tool call
  8. Server sends chat:stream_end when the response is complete
  9. Client can send additional chat:send messages with the same conversationId to 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

  1. Client opens WebSocket to /terminal
  2. Server creates a new PTY session (or reconnects to existing one)
  3. If reconnecting, server replays the output buffer as a single output message
  4. Server forwards all PTY output as output messages in real time
  5. Client sends input messages with raw keystrokes (including \r for Enter)
  6. 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:

ActionColor
create, restore, completesuccess
edit, runinfo
delete, cancelwarning
failerror

Connection Lifecycle

  1. Client opens WebSocket to /notifications
  2. Server registers the peer and sends connected
  3. Server broadcasts resource_change events as they occur
  4. On disconnect, the server automatically unregisters the peer
  5. 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.