24e0d0e799
Track viewers by endpoint id instead of a bare count. The JSON event stream gains viewer_joined / viewer_left (each carrying the id), replacing viewer_count; active/max still ride along so the count display is unchanged. The host screen now renders one row per connected viewer with a Kick button. Clicking it sends `kick <id>` to the headless child over a new stdin command channel, which the host turns into a per-viewer CancellationToken cancel; the existing teardown path then emits the leave, so a kick and a self-disconnect look identical downstream. The stdin channel only runs under --output json (the GUI shell-out) and on a detached OS thread, so a read parked on stdin can't hold up the host's Ctrl+C shutdown. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>