Fix idle session breaking resize and input on reconnect

Two bugs introduced in the idle tracker pause commit:

1. handleOutput() short-circuited entirely when idle, skipping
   connector.OnData() — so terminal output never reached clients
   even after reconnecting. Fix: still call connector.OnData()
   during idle; only skip the expensive tracker.Feed().

2. When a WebSocket client reconnects to an existing session,
   handleWebSocket never called UpdateConnector(), so the idle
   flag was never cleared and the tracker stayed paused. Fix:
   create a fresh connector and call session.UpdateConnector()
   on reconnect, which clears idle and rebuilds the tracker.
This commit is contained in:
GitHub Copilot
2026-02-18 15:09:31 +00:00
parent d9d012996e
commit 247c1a3340
4 changed files with 24 additions and 12 deletions
+12 -11
View File
@@ -791,17 +791,18 @@ t.Fatalf("replay mismatch: %q", got)
s.MarkIdle()
s.idleSince.Store(time.Now().Add(-idleTrackerThreshold - time.Second).UnixNano())
// Feed more output while idle — only replay should update
s.handleOutput([]byte(" world"))
if got := string(s.GetReplayBuffer()); got != "hello world" {
t.Fatalf("replay should accumulate while idle: %q", got)
}
conn.mu.Lock()
idleData := len(conn.data)
conn.mu.Unlock()
if idleData != 1 {
t.Fatalf("connector should NOT receive data while idle, got %d calls", idleData)
}
// Feed more output while idle — replay and connector still receive data,
// but the VT parser (tracker) is skipped.
s.handleOutput([]byte(" world"))
if got := string(s.GetReplayBuffer()); got != "hello world" {
t.Fatalf("replay should accumulate while idle: %q", got)
}
conn.mu.Lock()
idleData := len(conn.data)
conn.mu.Unlock()
if idleData != 2 {
t.Fatalf("connector should still receive data while idle, got %d calls", idleData)
}
// GetScreenSnapshot should rebuild tracker on-demand
snap2 := s.GetScreenSnapshot()
+1
View File
@@ -154,6 +154,7 @@ func (s *DockerExecSession) handleOutput(data []byte) {
if ts := s.idleSince.Load(); ts != 0 && time.Since(time.Unix(0, ts)) > idleTrackerThreshold {
if len(filtered) > 0 {
s.replay.Add(filtered)
connector.OnData(filtered)
}
return
}
+7
View File
@@ -515,6 +515,13 @@ func (s *LocalServer) handleWebSocket(w http.ResponseWriter, r *http.Request) {
session := s.sessionManager.GetSession(sessionID)
if session != nil && session.IsRunning() {
sessionCreated = true
// Clear idle state so the output pipeline resumes fully
connector := &localClientConnector{
server: s,
sessionID: sessionID,
routeKey: routeKey,
}
session.UpdateConnector(connector)
replay := daResponsePattern.ReplaceAll(session.GetReplayBuffer(), nil)
if len(replay) > 0 {
s.enqueueWSFrame(routeKey, websocket.BinaryMessage, replay)
+4 -1
View File
@@ -146,9 +146,12 @@ func (s *TerminalSession) handleOutput(data []byte) {
s.mu.Unlock()
filtered = FilterUnsupportedModes(filtered)
if ts := s.idleSince.Load(); ts != 0 && time.Since(time.Unix(0, ts)) > idleTrackerThreshold {
// No client connected — only maintain the replay buffer.
// No client connected — maintain replay buffer but skip VT parser.
// Still call the connector so data flows if a client just reconnected
// before MarkIdle was cleared.
if len(filtered) > 0 {
s.replay.Add(filtered)
connector.OnData(filtered)
}
return
}