Event Streaming & WebSockets
Relevant source files
The following files were used as context for generating this wiki page:
Opcode implements a robust event streaming architecture that allows for real-time interaction with the Claude Code CLI. This system is designed to work across two distinct environments: the Tauri desktop application and a standalone Web Server mode. By utilizing WebSockets for web-based communication and Tauri's internal IPC for desktop, the system ensures that long-running AI processes provide immediate, streaming feedback to the user interface.
The primary purpose of this module is to bridge the gap between the asynchronous nature of LLM execution and the requirement for a responsive GUI. It handles the lifecycle of a Claude command—from initiation and process spawning to real-time output parsing and eventual completion or cancellation.
System Architecture
The event streaming system is bifurcated based on the detected environment. In Desktop mode, it leverages Tauri's invoke and listen APIs. In Web mode, it utilizes a custom Rust backend (Axum) and WebSockets.
High-Level Communication Flow
The system uses a unified apiCall wrapper to abstract environment-specific logic, ensuring that UI components like ClaudeCodeSession.tsx remain platform-agnostic while receiving real-time updates.
Sources: src/lib/apiAdapter.ts:161-175, web_server.design.md:11-23
WebSocket Implementation (Web Mode)
When running in web mode, the application connects to a WebSocket endpoint at /ws/claude. This connection manages the entire lifecycle of a Claude execution session.
WebSocket Protocol Specification
Requests sent from the browser to the backend follow a specific JSON structure to define the command type and context:
| Field | Type | Description |
|---|---|---|
command_type | String | execute, continue, or resume |
project_path | String | The absolute path to the project directory |
prompt | String | The user's input/instruction |
model | String | The Claude model ID (e.g., claude-3-5-sonnet) |
session_id | String | Optional ID for resuming existing sessions |
Sources: web_server.design.md:52-60, src/lib/apiAdapter.ts:251-259
Event Processing Sequence
The sequence below illustrates how a prompt is processed and streamed back to the UI in Web mode:
The Rust backend captures stdout from the Claude process, wraps each line into a JSON packet, and forwards it to the frontend via the WebSocket.
Sources: web_server.design.md:69-80, src/lib/apiAdapter.ts:271-332
Event Handling & UI Integration
The frontend utilizes a custom hook or component-level logic to listen for streaming events. To maintain compatibility between Tauri and Web modes, the system implements a dynamic listen function.
Dynamic Listener Strategy
The application detects the environment and switches between Tauri's event bus and the browser's DOM event system:
const listen = (eventName: string, callback: (event: any) => void) => {
if (isTauriEnv()) {
return tauriListen(eventName, callback); // Desktop
} else {
// Web Mode: Use Custom DOM Events
const domHandler = (e: any) => callback({ payload: e.detail });
window.addEventListener(eventName, domHandler);
return Promise.resolve(() => window.removeEventListener(eventName, domHandler));
}
};
Sources: src/components/ClaudeCodeSession.tsx:43-68, src/lib/apiAdapter.ts:365-385
Message Payload Types
The streaming system handles several message subtypes to provide a rich UI experience:
| Message Type | Subtype / Payload | Description |
|---|---|---|
system | init | Contains session ID, model info, and available tools. |
assistant | tool_use | Indicates Claude is calling a tool (e.g., bash, edit). |
user | tool_result | Captured output of a tool execution sent back to the model. |
assistant | thinking | Internal reasoning/chain-of-thought from the model. |
summary | text | A high-level summary of the session progress. |
Sources: src/components/StreamMessage.tsx:100-145, src/components/ToolWidgets.tsx:645-660
Stream Parsing & Tool Visualization
As messages arrive via the stream, they are processed by the StreamMessage component and various ToolWidgets. This allows raw JSONL output from the Claude process to be rendered as interactive UI elements.
The Rendering Pipeline
- Capture:
apiAdapter.tsreceives a message via WebSocket or Tauri Event. - Dispatch: A
claude-outputevent is fired. - Accumulate:
ClaudeCodeSession.tsxupdates its internalmessagesstate array. - Transform:
StreamMessage.tsxevaluates the message type. - Render: Specific widgets (e.g.,
BashWidget,EditWidget,MCPWidget) are rendered based on thetool_usename.
Sources: src/components/ClaudeCodeSession.tsx:440-470, src/components/StreamMessage.tsx:150-220
Multi-Session Management (Web Mode)
The web server tracks active sessions using a HashMap in its application state. Each session is identified by a UUID. However, the design identifies a current limitation where the backend may dispatch generic events (e.g., claude-output) instead of session-scoped events (e.g., claude-output:${sessionId}), which is a critical area for ongoing development to support concurrent users.
Sources: web_server.design.md:121-135, src/components/ClaudeCodeSession.tsx:502-515
Cancellation & Error Handling
Graceful termination of streaming events is handled through dedicated endpoints and event types.
- Cancellation: Users can trigger
cancel_claude_executionvia the UI. In Desktop mode, this kills the process; in Web mode, it sends a request to the/api/sessions/{sessionId}/cancelendpoint. - Errors: If the stream fails or the Claude process exits with an error, a
claude-errorevent is dispatched. TheapiAdapter.tscaptures WebSocketonerrorandonclose(unexpected) events to notify the UI. - Timeouts/Retries: The WebSocket implementation includes state tracking to resolve or reject the initiation promise based on
completionorerrormessage types.
Sources: src/lib/apiAdapter.ts:320-355, web_server.design.md:143-150, src/components/ClaudeCodeSession.tsx:840-865
Summary
The Event Streaming & WebSockets module provides the real-time foundation of the Opcode application. By abstracting the transport layer (Tauri IPC vs. WebSockets), it allows the frontend to interact with the Claude CLI as a streaming service. This architecture supports complex features like tool execution progress, thinking-block visibility, and multi-step AI workflows while maintaining a responsive user interface.