fix(tools): wait for pipe readers before reaping detached bash shells#716
Merged
Conversation
Detaching a streaming bash command (ctrl+b, or `!cmd &`) could truncate its trailing output: the pipe-reader goroutines were still draining stdout/stderr into the shell buffer while the supervisor reaped the process with cmd.Wait, which closes the pipes on exit. shellJob.Run now waits for the readers to reach EOF before cmd.Wait, selecting on ctx so a kill or shutdown can't wedge when a grandchild is holding the pipe open. Both detach paths thread the readers-done signal through DetachToBackground; the old 20ms sleep band-aid is removed.
inference-gateway-releaser Bot
added a commit
that referenced
this pull request
Jul 3, 2026
## [0.128.0](v0.127.0...v0.128.0) (2026-07-03) ### 🚀 Features * **chat:** make status indicators selectable with /tools and /a2a views ([#732](#732)) ([6882de0](6882de0)), closes [#717](#717) [#717](#717) ### 🐛 Bug Fixes * **agent:** drop redundant Idle transition that errored on every run ([#726](#726)) ([469ec64](469ec64)), closes [#722](#722) [#725](#725) * **agent:** report session-cumulative total_turns in completion logs ([#728](#728)) ([0d8fb08](0d8fb08)), closes [#727](#727) * **a2a:** route active A2A reads and HasPending through the job supervisor ([#693](#693)) ([#721](#721)) ([dcd0670](dcd0670)), closes [#718](#718) [#719](#719) [#720](#720) * **keybindings:** stop "unknown keybinding action" log flood ([#724](#724)) ([c828426](c828426)), closes [#714](#714) [#722](#722) * **a2a:** unify background-job liveness, clear, and cancel on the supervisor contract ([#730](#730)) ([eb37fd0](eb37fd0)), closes [#693](#693) [#720](#720) [#718](#718) [#719](#719) * **logging:** use structured key/value form at misused call sites ([#729](#729)) ([cd96028](cd96028)), closes [#722](#722) [#724](#724) [#724](#724) [#723](#723) * **tools:** wait for pipe readers before reaping detached bash shells ([#716](#716)) ([e1d0d84](e1d0d84)), closes [#696](#696) ### ⚡ Performance Improvements * **tui:** cache per-entry rendering and keep the Update loop responsive ([#731](#731)) ([8c9db0d](8c9db0d)) ### 📚 Documentation * **a2a:** add SKILL.md documentation for Agent2Agent protocol ([defcd57](defcd57)) ### 🔧 Build System * **deps:** bump golang.org/x/net from 0.47.0 to 0.55.0 in /examples/mcp/mcp-server ([#715](#715)) ([990803b](990803b)) ### 🧹 Maintenance * **flox:** bump dev deps ([d0c8f6b](d0c8f6b))
Contributor
|
🎉 This PR is included in version 0.128.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
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
Detaching a streaming bash command to the background — chat
ctrl+b, or directexec!cmd &— could truncate its trailing output. The pipe-reader goroutines drain the command'sstdout/stderrinto the shared shell buffer, but the live*exec.Cmdwas handed to thejobs.Supervisor, whoseshellJob.Runcalledcmd.Wait()in a separate goroutine with no coordination. Peros/exec,Waitcloses the pipes on process exit, so output still buffered in the pipe at exit was lost. Thetime.Sleep(20ms)band-aid ran at detach time, unrelated to exit time, so it never addressed the race.Fix
shellJob.Runnow waits for the pipe readers to reach EOF beforecmd.Wait(), mirroring the already-correct foreground path (wg.Wait()thencmd.Wait()). The waitselects onctx.Done()soWindStop/Stop()can't wedge if a grandchild holds the pipe open (a detached shell has no timeout). Both detach callers thread a readers-done channel throughDetachToBackground.internal/domain/shell.go—ReadersDoneonBackgroundShell; widenedDetachToBackgroundinternal/services/background_shell_service.go— storereadersDoneon the shellinternal/services/jobs/shell_job.go— cancellable readers-done wait beforecmd.Wait()internal/agent/tools/bash.go,internal/services/directexec/bash.go— pass the signal; drop the 20ms sleepfake_background_shell_service.goTesting
shell_job_test.go: ordering (Run parks until the readers drain, even after the process has exited) and the cancellable escape (WindStopstill reaps when the readers never signal). Confirmed the latter deadlocks without thectx.Done()case.task build,go test -raceon the affected packages, fulltask test, andtask lint(0 issues) — all clean.Closes #696