9e4492fd74
When a background terminal() command backgrounds its own long-lived child (`node server.js &`, `sleep 300 &`), the grandchild inherits the write end of the reader thread's stdout pipe. The direct bash child exits promptly, but the pipe never reaches EOF while the grandchild lives — so `_reader_loop`'s blocking `read1()` parked the thread forever, `session.exited` never flipped on its own, and `notify_on_complete` was silently lost. `_reconcile_local_exit` (#17327) only runs lazily from poll()/wait(), so nothing autonomous ever surfaced the exit; each occurrence also leaked a reader thread and pipe fd for the grandchild's lifetime. Fix: on POSIX, drain via select() with a short poll interval and stop shortly after the direct child exits even if the pipe hasn't EOF'd — the same pattern the foreground path uses in tools/environments/base.py::_wait_for_process (#8340). Windows pipes don't support select(), so the blocking path is kept there with the existing lazy reconcile as the safety net; mocked/iterator stdout streams (no usable fileno) also keep the historical path. Fixes #68915