Completed
on 8 Jan 2026, 1:01 pm

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

ConnectionCurrentNew
Browser ↔ KonuiSSE (per-request)WebSocket (persistent)
Konui ↔ KonsoleSSE (per-turn)SSE (unchanged)
Konsole ↔ Claudestdio/stream-jsonstdio (unchanged)

Implementation Plan

  1. 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)
  2. Add WS endpoint to router.ts
    • GET /api/konsole/dashboard/ws - WebSocket upgrade
    • Similar pattern to /api/admin/ws
  3. Update dashboard client
    • Replace EventSource with WebSocket
    • Connection never closes (reconnects like admin broadcast)
    • Status bar driven by WS signals, not stream state
  4. 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"
}
DashboardReportsKontasksFlowsDecisionsSessionsTelemetryLogs + Go