diff --git a/server/src/agents/pty.rs b/server/src/agents/pty.rs index e2d1417e..ef6fb906 100644 --- a/server/src/agents/pty.rs +++ b/server/src/agents/pty.rs @@ -245,13 +245,16 @@ fn run_agent_pty_blocking( // via recv_timeout: if no output arrives within the configured window // the process is killed and the agent is marked Failed. let (line_tx, line_rx) = std::sync::mpsc::channel::>(); - std::thread::spawn(move || { + let sid_for_reader = story_id.to_string(); + let aname_for_reader = agent_name.to_string(); + let reader_handle = std::thread::spawn(move || { let buf_reader = BufReader::new(reader); for line in buf_reader.lines() { if line_tx.send(line).is_err() { break; } } + slog!("[agent:{sid_for_reader}:{aname_for_reader}] Reader thread exiting"); }); let timeout_dur = if inactivity_timeout_secs > 0 { @@ -424,6 +427,15 @@ fn run_agent_pty_blocking( let _ = child.kill(); let _ = child.wait(); + // Wait for the reader thread to finish so it releases the cloned PTY + // master fd before we return. Without this, the next PTY spawn for the + // same story can collide with a still-open fd from this session (#453). + slog!("[agent:{story_id}:{agent_name}] Waiting for reader thread to exit"); + if let Err(e) = reader_handle.join() { + slog!("[agent:{story_id}:{agent_name}] Reader thread panicked: {e:?}"); + } + slog!("[agent:{story_id}:{agent_name}] Reader thread joined"); + slog!( "[agent:{story_id}:{agent_name}] Done. Session: {:?}", session_id