Repository navigation
feat: parallel setup, WebSocket I/O, per-session locks - #48
Merged
Merged
Conversation
Apply PR #47 from David O'Keeffe with the following enhancements: - Parallel setup via ThreadPoolExecutor (~5x faster startup) - Per-session locks (reduced contention vs global lock) - WebSocket terminal I/O via Flask-SocketIO (eliminates 50-200ms polling latency) Fixes applied on top of PR #47: - Fix session timeout: update last_poll_time on WS output emit - Vendor Socket.IO client to /static/lib/ (avoid CDN dependency) - Restrict CORS: cors_allowed_origins=[] (same-origin only) - Increase thread pool: 8→16 for WS connection headroom - Adjust gunicorn timeout: 120s→60s (balanced for WS + hung-worker detection) - Clear output buffer on WS join (prevent duplicate output on transport switch) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lers The WS connect/disconnect/error handlers and terminal_output/session_exited event handlers referenced `panes` (undefined) instead of `getAllPanes()`. This caused WS rooms to never be joined and poll-worker to never stop, resulting in both WS and HTTP polling running simultaneously. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three gaps found during WS↔HTTP parity audit:
1. Idle session reaping: With HTTP polling stopped, nothing updated
last_poll_time for idle WS sessions. Added client-side 30s heartbeat
(batched session_ids) and server-side handler to refresh timestamps.
2. SIGTERM notification: HTTP poll detects shutting_down flag on next
poll. WS clients had no equivalent — they'd just see a disconnect.
Added socketio.emit('shutting_down') in SIGTERM handler for immediate
notification before the connection drops.
3. Background tab throttling: Browsers throttle setInterval in background
tabs (1min+ delays). Added immediate WS heartbeat on visibilitychange
to reset the 5-min session timeout clock when tab goes to/from background.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5 tasks
sendBeacon with a raw string sends Content-Type: text/plain, which Flask rejects with 415 Unsupported Media Type. The heartbeat sendBeacon already uses Blob with application/json — apply the same pattern to session close. Without this fix, closed sessions linger until the 5-min cleanup thread reaps them instead of being terminated immediately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Owner
Author
datasciencemonkey
added a commit
that referenced
this pull request
Mar 10, 2026
Document the current state after PR #48: WebSocket I/O (primary) with HTTP polling fallback, parallel agent setup, new API endpoints, Socket.IO events, updated gunicorn config, and expanded project structure.
This was referenced Mar 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Fixes applied on top of PR #47
Critical bugs (3)
read_pty_output()never updatedlast_poll_timeduring WS output — sessions reaped while user watches outputlast_poll_timeon every PTY read under per-session lockpanes→getAllPanes()panesvariable —forEachsilently did nothing, WS rooms never joined, polling never stoppedgetAllPanes()(tabs.flatMap(t => t.panes))WS↔HTTP parity gaps (3)
heartbeatevent every 30s; server handler refresheslast_poll_timeshutting_downflag on next poll; WS clients just saw a disconnecthandle_sigtermnow broadcastsshutting_downevent to all WS clientssetIntervalto 1min+ in background tabs; WS heartbeat could miss 5-min windowvisibilitychangeresets timeout clockSecurity & deployment (2)
cdn.socket.iomay be blocked on Databricks Apps/static/lib/socket.io.min.jscors_allowed_origins='*'allowed cross-origin WScors_allowed_origins=[](same-origin only)Reliability (2)
output_bufferonjoin_sessionWS↔HTTP Feature Parity Audit (12/12 pass)
terminal_outputevent → pane bysession_id/api/output-batch→ distribute bysession_idsession_exitedevent →cleanupPane()data.exitedflag →session_endedmsgterminal_inputevent →os.write(fd)/api/inputPOST →os.write(fd)terminal_resizeevent →ioctl TIOCSWINSZ/api/resizePOST →ioctl TIOCSWINSZheartbeatevent (batched)last_poll_timevisibilitychangeshutting_downbroadcast eventshutting_downflag in poll response_check_ws_authorization()on connectdisconnect→ restart poll-workerconnect→ join rooms + stop pollingjoin_sessionifwsConnectedstart_pollif notwsConnectedleave_session+stop_poll+sendBeaconstop_poll+sendBeaconjoin_room/room=session_id)session_id-keyed output dictCommits
f9e20dd— Apply PR feat: Parallel setup, WebSocket I/O, per-session locks #47 + 6 original fixeseed8076— Fixpanes→getAllPanes()in all WS handlersa5fdf4b— Add WS heartbeat, SIGTERM broadcast, background tab safetySupersedes
PR #47 — incorporates all changes with conflict resolution and 10 fixes.
Test plan
socket.io.min.jsserves correctly[ws] Connectedin console, no/api/output-batchspam🤖 Generated with Claude Code