← Back to Kontasks
Plan: WebSocket Console Connection
Replace SSE with persistent WebSocket for browser-konui console connection
Output
WebSocket Console Connection Plan
Key Insight
You're right - kontask delivery is async and arrives after 'done'. The real problem is treating SSE like a request-response pattern when it should be an event stream. The admin broadcast WebSocket is rock solid because it's always connected regardless of turn state.
Loading diagram...
Architecture Change
| Connection | Current | New |
|---|---|---|
| Browser ↔ Konui | SSE (per-request) | WebSocket (persistent) |
| Konui ↔ Konsole | SSE (per-turn) | SSE (unchanged) |
| Konsole ↔ Claude | stdio/stream-json | stdio (unchanged) |
Implementation Plan
- Create
console-websocket.ts- WebSocket manager (like admin-broadcast)- Handles upgrade, maintains per-user connections
- Receives events from konui SSE proxy, forwards to browser
- Sends status signals (turn_start, turn_end, kontask_ready)
- Add WS endpoint to router.ts
GET /api/konsole/dashboard/ws- WebSocket upgrade- Similar pattern to
/api/admin/ws
- Update dashboard client
- Replace EventSource with WebSocket
- Connection never closes (reconnects like admin broadcast)
- Status bar driven by WS signals, not stream state
- Keep konui→konsole SSE
- konui opens SSE to konsole for each turn
- Proxies events through WebSocket to browser
- Turn complete = close SSE, but WS stays open
WebSocket Message Types
// Server → Browser
{ type: 'turn_start', requestId, prompt }
{ type: 'event', data: { ... } } // Proxied from konsole SSE
{ type: 'turn_end', requestId, success }
{ type: 'kontask_ready', kontaskId } // Async kontask delivery
{ type: 'status', turnActive, elapsed }
{ type: 'ping' }
// Browser → Server
{ type: 'send_prompt', prompt, images? }
{ type: 'stop_turn' }
{ type: 'pong' }Benefits
- Always connected - No reconnect dance on turn end
- Clean separation - Turn state vs connection state
- Kontask delivery - Can push kontask_ready whenever it arrives
- Status bar - Server tells browser when turn is active
- Mobile-friendly - WS reconnects naturally on visibility change
Quick Actions
Original Request
No layout configured
Details
Type
General
Status
Completed
Scope
vibetools
Tags
consolewebsocketreliabilityarchitecture
Created
8 Jan 2026, 1:01 pm
Updated
8 Jan 2026, 1:01 pm
Timing
⏱️ 66.7s
0 tools
● TTFE 0%
● Thinking 100%
● Tools 0%
Created By
claude
Raw Data
{
"id": "762021ee-a0e9-46a3-8284-99176bcb3fd5",
"type": "general",
"status": "completed",
"title": "Plan: WebSocket Console Connection",
"description": "Replace SSE with persistent WebSocket for browser-konui console connection",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:sans-serif;\"><h2 style=\"color:#fff;margin:0 0 16px 0;\">WebSocket Console Connection Plan</h2><div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\"><h4 style=\"margin:0 0 12px 0;color:#22c55e;\">Key Insight</h4><p style=\"margin:0;line-height:1.6;\">You're right - kontask delivery is <strong>async</strong> and arrives after 'done'. The real problem is treating SSE like a request-response pattern when it should be an <strong>event stream</strong>. The admin broadcast WebSocket is rock solid because it's <strong>always connected</strong> regardless of turn state.</p></div><div id=\"arch-diagram\" style=\"background:#1e1e1e;padding:16px;border-radius:8px;margin-bottom:16px;min-height:200px;\"><div style=\"color:#888;\">Loading diagram...</div></div><script>(function(){const definition=`%%{init:{'theme':'dark'}}%%\nflowchart LR\n subgraph Browser[\"Browser\"]\n UI[Dashboard UI]\n end\n subgraph Konui[\"Konui :8666\"]\n WS[WebSocket Handler]\n SSE[SSE Proxy]\n end\n subgraph Konsole[\"Konsole :8020\"]\n CL[Claude Process]\n end\n UI <-->|\"WebSocket\\n(persistent)\"| WS\n SSE -->|\"SSE\\n(per-turn)\"| WS\n Konsole -->|\"SSE\\n(unchanged)\"| SSE\n style Browser fill:#1e3a5f,stroke:#3b82f6,color:#fff\n style Konui fill:#1e3a3f,stroke:#22c55e,color:#fff\n style Konsole fill:#2d1f3d,stroke:#8b5cf6,color:#fff\n style WS fill:#22c55e,stroke:#16a34a,color:#000\n style SSE fill:#f59e0b,stroke:#d97706,color:#000\n style UI fill:#3b82f6,stroke:#2563eb,color:#fff\n style CL fill:#8b5cf6,stroke:#7c3aed,color:#fff\n`;window.renderMermaidDiagram('arch-diagram',definition);})();</script><div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\"><h4 style=\"margin:0 0 12px 0;color:#3b82f6;\">Architecture Change</h4><table style=\"width:100%;border-collapse:collapse;font-size:0.9rem;\"><tr style=\"border-bottom:1px solid #444;\"><th style=\"text-align:left;padding:8px;color:#9ca3af;\">Connection</th><th style=\"text-align:left;padding:8px;color:#9ca3af;\">Current</th><th style=\"text-align:left;padding:8px;color:#9ca3af;\">New</th></tr><tr style=\"border-bottom:1px solid #333;\"><td style=\"padding:8px;\">Browser ↔ Konui</td><td style=\"padding:8px;color:#ef4444;\">SSE (per-request)</td><td style=\"padding:8px;color:#22c55e;\">WebSocket (persistent)</td></tr><tr style=\"border-bottom:1px solid #333;\"><td style=\"padding:8px;\">Konui ↔ Konsole</td><td style=\"padding:8px;color:#f59e0b;\">SSE (per-turn)</td><td style=\"padding:8px;color:#f59e0b;\">SSE (unchanged)</td></tr><tr><td style=\"padding:8px;\">Konsole ↔ Claude</td><td style=\"padding:8px;color:#8b5cf6;\">stdio/stream-json</td><td style=\"padding:8px;color:#8b5cf6;\">stdio (unchanged)</td></tr></table></div><div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\"><h4 style=\"margin:0 0 12px 0;color:#f59e0b;\">Implementation Plan</h4><ol style=\"margin:0;padding-left:20px;line-height:2;\"><li><strong>Create <code>console-websocket.ts</code></strong> - WebSocket manager (like admin-broadcast)<ul style=\"margin:4px 0 8px 0;color:#9ca3af;\"><li>Handles upgrade, maintains per-user connections</li><li>Receives events from konui SSE proxy, forwards to browser</li><li>Sends status signals (turn_start, turn_end, kontask_ready)</li></ul></li><li><strong>Add WS endpoint to router.ts</strong><ul style=\"margin:4px 0 8px 0;color:#9ca3af;\"><li><code>GET /api/konsole/dashboard/ws</code> - WebSocket upgrade</li><li>Similar pattern to <code>/api/admin/ws</code></li></ul></li><li><strong>Update dashboard client</strong><ul style=\"margin:4px 0 8px 0;color:#9ca3af;\"><li>Replace EventSource with WebSocket</li><li>Connection never closes (reconnects like admin broadcast)</li><li>Status bar driven by WS signals, not stream state</li></ul></li><li><strong>Keep konui→konsole SSE</strong><ul style=\"margin:4px 0 8px 0;color:#9ca3af;\"><li>konui opens SSE to konsole for each turn</li><li>Proxies events through WebSocket to browser</li><li>Turn complete = close SSE, but WS stays open</li></ul></li></ol></div><div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\"><h4 style=\"margin:0 0 12px 0;color:#8b5cf6;\">WebSocket Message Types</h4><pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;font-family:monospace;font-size:0.85rem;overflow-x:auto;color:#e0e0e0;line-height:1.6;\">// Server → Browser\n{ type: 'turn_start', requestId, prompt }\n{ type: 'event', data: { ... } } // Proxied from konsole SSE\n{ type: 'turn_end', requestId, success }\n{ type: 'kontask_ready', kontaskId } // Async kontask delivery\n{ type: 'status', turnActive, elapsed }\n{ type: 'ping' }\n\n// Browser → Server\n{ type: 'send_prompt', prompt, images? }\n{ type: 'stop_turn' }\n{ type: 'pong' }</pre></div><div style=\"background:#2d2d2d;padding:16px;border-radius:6px;\"><h4 style=\"margin:0 0 12px 0;color:#fff;\">Benefits</h4><ul style=\"margin:0;padding-left:20px;line-height:1.8;\"><li><strong>Always connected</strong> - No reconnect dance on turn end</li><li><strong>Clean separation</strong> - Turn state vs connection state</li><li><strong>Kontask delivery</strong> - Can push kontask_ready whenever it arrives</li><li><strong>Status bar</strong> - Server tells browser when turn is active</li><li><strong>Mobile-friendly</strong> - WS reconnects naturally on visibility change</li></ul></div></div>",
"requestedAt": "2026-01-08T02:55:00.000Z",
"requestId": "48c3d815-08bf-4bc1-955c-c844fad022a7",
"choices": [
{
"label": "Start implementation",
"value": "Start implementing the WebSocket console connection - create console-websocket.ts and add the WS upgrade handler to router.ts",
"primary": true
},
{
"label": "Review admin-broadcast first",
"value": "Let me review the full admin-broadcast.ts implementation to understand the WebSocket patterns before starting"
},
{
"label": "More planning",
"value": "I have questions about this approach before we start implementing"
}
],
"turnTiming": {
"totalMs": 66728,
"ttfeMs": 54,
"thinkingMs": 66674,
"toolExecutionMs": 0,
"toolCallCount": 0,
"thinkingPct": 100,
"toolsPct": 0,
"ttfePct": 0
}
},
"createdBy": "claude",
"createdAt": "2026-01-08T03:01:26.906Z",
"updatedAt": "2026-01-08T03:01:37.749Z",
"requestId": "48c3d815-08bf-4bc1-955c-c844fad022a7",
"scope": "vibetools",
"tags": [
"console",
"websocket",
"reliability",
"architecture"
],
"targetUser": "claude"
}