← Back to Kontasks
Multi-Viewer Sessions: One Session, Many Browsers
Design for synchronized session viewing across multiple devices - mobile, desktop, seamless handoff
Output
📱💻 Multi-Viewer Sessions
One session, many viewers. Identical state. Seamless handoff.
The Vision
09:00 - Submit prompt on mobile while commuting 09:05 - Arrive at office, open Mac Studio 09:05 - See exact same console output, live streaming 09:10 - Claude finishes, kontask appears on both devices 09:11 - Approve kontask on Mac, mobile shows "approved"
Architecture: Pub/Sub Session Channels
┌──────────────────────────────────────────────────────────────────┐ │ KONUI HUB │ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Session Channel: johnathon/sess_abc123 │ │ │ │ │ │ │ │ Events → [broadcast to all subscribers] │ │ │ │ │ │ │ └───────────┬────────────────────┬────────────────────┬───────────┘ │ │ │ │ │ │ └──────────────┼────────────────────┼────────────────────┼───────────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Mobile │ │Mac Studio│ │ iPad │ │ (viewer) │ │ (viewer) │ │ (viewer) │ └──────────┘ └──────────┘ └──────────┘ ↑ ↑ ↑ WebSocket WebSocket WebSocket (same events) (same events) (same events)
Session Channel Protocol
1. SUBSCRIBE TO SESSION → { type: "subscribe", sessionId: "sess_abc123" } ← { type: "subscribed", viewerCount: 2, lastSeq: 1547 } 2. CATCH UP (if joining mid-session) → { type: "catchup", fromSeq: 0 } // agent replays ← { type: "event", seq: 1, ... } ← { type: "event", seq: 2, ... } ← { type: "caught_up", atSeq: 1547 } 3. LIVE EVENTS (broadcast to all viewers) ← { type: "event", seq: 1548, data: { type: "text", ... } } ← { type: "event", seq: 1549, data: { type: "tool_call", ... } } 4. VIEWER ACTIONS (any viewer can act) → { type: "prompt", content: "..." } // from any device ← { type: "event", source: "mobile", ... } // all see it 5. VIEWER PRESENCE ← { type: "viewer_joined", device: "Mac Studio", count: 3 } ← { type: "viewer_left", device: "iPad", count: 2 }
Key Features
| Feature | Behavior |
|---|---|
| Instant sync | Same event, same millisecond, all devices |
| Late join | Catch up from agent journal (full history) |
| Any device can act | Submit prompt, approve kontask from anywhere |
| Presence awareness | See who else is watching (📱 💻 📲) |
| Cursor sync | Optional: see other viewers' scroll position |
| Input focus | Show "johnathon is typing..." on other devices |
State Synchronization
// Synchronized state (identical across all viewers) interface SessionState { events: Event[] // Full event history todos: Todo[] // Current todo list status: Status // idle | working | stalled lastSeq: number // Latest event sequence } // Local state (per-viewer, not synced) interface ViewerState { scrollPosition: number // Where they're reading inputDraft: string // Unsent text in textarea expandedSections: [] // UI preferences }
UI Indicators
// Status bar shows connected viewers ┌────────────────────────────────────────────────────────────┐ │ ● Working [3|5 ▓▓▓░░] Reading file... 📱💻 12s │ └────────────────────────────────────────────────────────────┘ ↑↑ viewer icons // Toast when viewer joins/leaves ┌──────────────────────────────┐ │ 💻 Mac Studio joined │ └──────────────────────────────┘
💡 Implementation Note
This is much simpler with the agent-stores-state architecture.
Hub's job: Manage session channels, route events to subscribers
Agent's job: Store events, replay on request
Browser's job: Subscribe, render, send actions
No complex state sync — agent is single source of truth, all viewers just replay.
What's Next?
Choose an action:
Quick Actions
Original Request
No layout configured
Details
Type
General
Status
Completed
Scope
vibetools
Tags
agentsessionsmulti-devicesync
Created
5 Jan 2026, 3:53 pm
Updated
5 Jan 2026, 3:53 pm
Timing
⏱️ 125.6s
1 tools
● TTFE 4%
● Thinking 96%
● Tools 0%
Created By
claude
Raw Data
{
"id": "98e5f965-2df2-4666-8dbc-a2e1bd392c23",
"type": "general",
"status": "completed",
"title": "Multi-Viewer Sessions: One Session, Many Browsers",
"description": "Design for synchronized session viewing across multiple devices - mobile, desktop, seamless handoff",
"context": {
"output": "<div style=\"background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;font-family:'Consolas','Monaco',monospace;\">\n<h2 style=\"font-family:sans-serif;color:#fff;margin:0 0 16px 0;\">📱💻 Multi-Viewer Sessions</h2>\n\n<p style=\"color:#9ca3af;font-family:sans-serif;margin:0 0 16px 0;\">One session, many viewers. Identical state. Seamless handoff.</p>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;font-family:sans-serif;\">The Vision</h3>\n\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;overflow-x:auto;font-size:0.85rem;color:#abb2bf;margin:0;\">\n<span style=\"color:#98c379;\">09:00</span> - Submit prompt on <span style=\"color:#c678dd;\">mobile</span> while commuting\n<span style=\"color:#98c379;\">09:05</span> - Arrive at office, open <span style=\"color:#61afef;\">Mac Studio</span>\n<span style=\"color:#98c379;\">09:05</span> - See exact same console output, live streaming\n<span style=\"color:#98c379;\">09:10</span> - Claude finishes, kontask appears on <span style=\"color:#e5c07b;\">both devices</span>\n<span style=\"color:#98c379;\">09:11</span> - Approve kontask on Mac, mobile shows \"approved\"\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;font-family:sans-serif;\">Architecture: Pub/Sub Session Channels</h3>\n\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;overflow-x:auto;font-size:0.8rem;color:#abb2bf;margin:0;\">\n<span style=\"color:#7f848e;\">┌──────────────────────────────────────────────────────────────────┐</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#61afef;\">KONUI HUB</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#e5c07b;\">┌──────────────────────────────────────────────────────────┐</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#e5c07b;\">│ Session Channel: johnathon/sess_abc123 │</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#e5c07b;\">│ │</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#e5c07b;\">│ Events → [broadcast to all subscribers] │</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#e5c07b;\">│ │</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#e5c07b;\">└───────────┬────────────────────┬────────────────────┬───────────┘</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">│</span> <span style=\"color:#7f848e;\">│</span> <span style=\"color:#7f848e;\">│</span> <span style=\"color:#7f848e;\">│</span> <span style=\"color:#7f848e;\">│</span>\n<span style=\"color:#7f848e;\">└──────────────┼────────────────────┼────────────────────┼───────────┘</span>\n <span style=\"color:#7f848e;\">│</span> <span style=\"color:#7f848e;\">│</span> <span style=\"color:#7f848e;\">│</span>\n <span style=\"color:#7f848e;\">▼</span> <span style=\"color:#7f848e;\">▼</span> <span style=\"color:#7f848e;\">▼</span>\n <span style=\"color:#c678dd;\">┌──────────┐</span> <span style=\"color:#61afef;\">┌──────────┐</span> <span style=\"color:#98c379;\">┌──────────┐</span>\n <span style=\"color:#c678dd;\">│ Mobile │</span> <span style=\"color:#61afef;\">│Mac Studio│</span> <span style=\"color:#98c379;\">│ iPad │</span>\n <span style=\"color:#c678dd;\">│ (viewer) │</span> <span style=\"color:#61afef;\">│ (viewer) │</span> <span style=\"color:#98c379;\">│ (viewer) │</span>\n <span style=\"color:#c678dd;\">└──────────┘</span> <span style=\"color:#61afef;\">└──────────┘</span> <span style=\"color:#98c379;\">└──────────┘</span>\n <span style=\"color:#7f848e;\">↑</span> <span style=\"color:#7f848e;\">↑</span> <span style=\"color:#7f848e;\">↑</span>\n <span style=\"color:#7f848e;\">WebSocket</span> <span style=\"color:#7f848e;\">WebSocket</span> <span style=\"color:#7f848e;\">WebSocket</span>\n <span style=\"color:#7f848e;\">(same events)</span> <span style=\"color:#7f848e;\">(same events)</span> <span style=\"color:#7f848e;\">(same events)</span>\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;font-family:sans-serif;\">Session Channel Protocol</h3>\n\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;overflow-x:auto;font-size:0.85rem;color:#abb2bf;margin:0;\">\n<span style=\"color:#c678dd;\">1. SUBSCRIBE TO SESSION</span>\n <span style=\"color:#98c379;\">→ { type: \"subscribe\", sessionId: \"sess_abc123\" }</span>\n <span style=\"color:#61afef;\">← { type: \"subscribed\", viewerCount: 2, lastSeq: 1547 }</span>\n\n<span style=\"color:#c678dd;\">2. CATCH UP (if joining mid-session)</span>\n <span style=\"color:#98c379;\">→ { type: \"catchup\", fromSeq: 0 }</span> <span style=\"color:#7f848e;\">// agent replays</span>\n <span style=\"color:#61afef;\">← { type: \"event\", seq: 1, ... }</span>\n <span style=\"color:#61afef;\">← { type: \"event\", seq: 2, ... }</span>\n <span style=\"color:#61afef;\">← { type: \"caught_up\", atSeq: 1547 }</span>\n\n<span style=\"color:#c678dd;\">3. LIVE EVENTS (broadcast to all viewers)</span>\n <span style=\"color:#61afef;\">← { type: \"event\", seq: 1548, data: { type: \"text\", ... } }</span>\n <span style=\"color:#61afef;\">← { type: \"event\", seq: 1549, data: { type: \"tool_call\", ... } }</span>\n\n<span style=\"color:#c678dd;\">4. VIEWER ACTIONS (any viewer can act)</span>\n <span style=\"color:#98c379;\">→ { type: \"prompt\", content: \"...\" }</span> <span style=\"color:#7f848e;\">// from any device</span>\n <span style=\"color:#61afef;\">← { type: \"event\", source: \"mobile\", ... }</span> <span style=\"color:#7f848e;\">// all see it</span>\n\n<span style=\"color:#c678dd;\">5. VIEWER PRESENCE</span>\n <span style=\"color:#61afef;\">← { type: \"viewer_joined\", device: \"Mac Studio\", count: 3 }</span>\n <span style=\"color:#61afef;\">← { type: \"viewer_left\", device: \"iPad\", count: 2 }</span>\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;font-family:sans-serif;\">Key Features</h3>\n\n<table style=\"width:100%;border-collapse:collapse;font-size:0.85rem;\">\n<tr style=\"background:#1a1a1a;\">\n<th style=\"padding:8px;text-align:left;color:#fff;\">Feature</th>\n<th style=\"padding:8px;text-align:left;color:#fff;\">Behavior</th>\n</tr>\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px;color:#c678dd;\">Instant sync</td>\n<td style=\"padding:8px;color:#e0e0e0;\">Same event, same millisecond, all devices</td>\n</tr>\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px;color:#c678dd;\">Late join</td>\n<td style=\"padding:8px;color:#e0e0e0;\">Catch up from agent journal (full history)</td>\n</tr>\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px;color:#c678dd;\">Any device can act</td>\n<td style=\"padding:8px;color:#e0e0e0;\">Submit prompt, approve kontask from anywhere</td>\n</tr>\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px;color:#c678dd;\">Presence awareness</td>\n<td style=\"padding:8px;color:#e0e0e0;\">See who else is watching (📱 💻 📲)</td>\n</tr>\n<tr style=\"border-bottom:1px solid #444;\">\n<td style=\"padding:8px;color:#c678dd;\">Cursor sync</td>\n<td style=\"padding:8px;color:#e0e0e0;\">Optional: see other viewers' scroll position</td>\n</tr>\n<tr>\n<td style=\"padding:8px;color:#c678dd;\">Input focus</td>\n<td style=\"padding:8px;color:#e0e0e0;\">Show \"johnathon is typing...\" on other devices</td>\n</tr>\n</table>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;font-family:sans-serif;\">State Synchronization</h3>\n\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;overflow-x:auto;font-size:0.85rem;color:#abb2bf;margin:0;\">\n<span style=\"color:#7f848e;\">// Synchronized state (identical across all viewers)</span>\n<span style=\"color:#e5c07b;\">interface SessionState</span> {\n <span style=\"color:#e06c75;\">events</span>: Event[] <span style=\"color:#7f848e;\">// Full event history</span>\n <span style=\"color:#e06c75;\">todos</span>: Todo[] <span style=\"color:#7f848e;\">// Current todo list</span>\n <span style=\"color:#e06c75;\">status</span>: Status <span style=\"color:#7f848e;\">// idle | working | stalled</span>\n <span style=\"color:#e06c75;\">lastSeq</span>: <span style=\"color:#98c379;\">number</span> <span style=\"color:#7f848e;\">// Latest event sequence</span>\n}\n\n<span style=\"color:#7f848e;\">// Local state (per-viewer, not synced)</span>\n<span style=\"color:#e5c07b;\">interface ViewerState</span> {\n <span style=\"color:#e06c75;\">scrollPosition</span>: <span style=\"color:#98c379;\">number</span> <span style=\"color:#7f848e;\">// Where they're reading</span>\n <span style=\"color:#e06c75;\">inputDraft</span>: <span style=\"color:#98c379;\">string</span> <span style=\"color:#7f848e;\">// Unsent text in textarea</span>\n <span style=\"color:#e06c75;\">expandedSections</span>: [] <span style=\"color:#7f848e;\">// UI preferences</span>\n}\n</pre>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h3 style=\"color:#e5c07b;margin:0 0 12px 0;font-family:sans-serif;\">UI Indicators</h3>\n\n<pre style=\"background:#1a1a1a;padding:12px;border-radius:4px;overflow-x:auto;font-size:0.85rem;color:#abb2bf;margin:0;\">\n<span style=\"color:#7f848e;\">// Status bar shows connected viewers</span>\n┌────────────────────────────────────────────────────────────┐\n│ <span style=\"color:#98c379;\">● Working</span> [3|5 ▓▓▓░░] Reading file... <span style=\"color:#c678dd;\">📱</span><span style=\"color:#61afef;\">💻</span> <span style=\"color:#7f848e;\">12s</span> │\n└────────────────────────────────────────────────────────────┘\n <span style=\"color:#7f848e;\">↑↑ viewer icons</span>\n\n<span style=\"color:#7f848e;\">// Toast when viewer joins/leaves</span>\n┌──────────────────────────────┐\n│ <span style=\"color:#61afef;\">💻 Mac Studio joined</span> │\n└──────────────────────────────┘\n</pre>\n</div>\n\n<div style=\"background:#1a3d1a;border:1px solid #2d5a2d;padding:16px;border-radius:6px;margin-bottom:16px;\">\n<h4 style=\"color:#98c379;margin:0 0 8px 0;font-family:sans-serif;\">💡 Implementation Note</h4>\n<p style=\"color:#e0e0e0;margin:0;line-height:1.6;\">This is <strong>much simpler</strong> with the agent-stores-state architecture.<br/><br/>\n<strong>Hub's job:</strong> Manage session channels, route events to subscribers<br/>\n<strong>Agent's job:</strong> Store events, replay on request<br/>\n<strong>Browser's job:</strong> Subscribe, render, send actions<br/><br/>\nNo complex state sync — agent is single source of truth, all viewers just replay.</p>\n</div>\n\n<div style=\"background:#2d2d2d;padding:16px;border-radius:6px;\">\n<h4 style=\"margin:0 0 8px 0;color:#fff;font-family:sans-serif;\">What's Next?</h4>\n<p style=\"color:#9ca3af;margin:0;font-family:sans-serif;\">Choose an action:</p>\n</div>\n</div>",
"requestedAt": "2026-01-05T03:55:00.000Z",
"requestId": "81270290-f479-4f2b-9adb-82c7a877af44",
"choices": [
{
"label": "Design WebSocket hub",
"value": "Design the WebSocket session channel hub in konui - subscription management, event routing, presence",
"primary": true
},
{
"label": "Add to VIBE.md",
"value": "Add the complete Konduct agent architecture (multi-viewer, session caching, replay) to VIBE.md backlog"
},
{
"label": "Prototype presence",
"value": "Prototype the viewer presence feature - show connected devices in status bar"
}
],
"turnTiming": {
"totalMs": 125562,
"ttfeMs": 4891,
"thinkingMs": 120307,
"toolExecutionMs": 363,
"toolCallCount": 1,
"thinkingPct": 96,
"toolsPct": 0,
"ttfePct": 4
}
},
"createdBy": "claude",
"createdAt": "2026-01-05T05:53:42.834Z",
"updatedAt": "2026-01-05T05:53:54.717Z",
"requestId": "81270290-f479-4f2b-9adb-82c7a877af44",
"scope": "vibetools",
"tags": [
"agent",
"sessions",
"multi-device",
"sync"
],
"targetUser": "claude"
}