Uh oh!
There was an error while loading. Please reload this page.
fix: settle shell completion on process exit instead of pipe EOF - #44601
fix: settle shell completion on process exit instead of pipe EOF#44601linsmod wants to merge 2 commits into
Conversation
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
The following comment was made by an LLM, it may be inaccurate: Found a related PR: PR #42275 — fix(core): resolve child process exit signal on exit event This PR is directly related but incomplete compared to PR #44601. According to the PR description itself, #42275 only fixes the first layer (spawner No other duplicate PRs found for this specific issue. |
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
Issue for this PR
Closes#20902
Type of change
What does this PR do?
The bash/shell tool reports a command as done only when the spawned child's stdio pipes fully close (
closeevent). When a command leaves a background descendant alive (gradle daemon, adb server, dev server,Start-Processchildren), that descendant inherits the pipe and keeps it open after the direct child exits, so the tool never sees EOF and blocks until timeout/abort. Same root cause as #22012, #24784, #29822, #32504, #37838, #42524, #43910.It's two problems stacked:
cross-spawn-spawner.tssettlesexitCodefromcloseinstead ofexit.AppProcess.run(Effect.all([collectStream, exitCode])) and the V1ShellToolstream reader both wait for the pipe to close.Changes:
exitCodefrom theexitevent, keepcloseas a fallback.process.ts: newcollectBounded— reads the stream concurrently (avoids a pipe-full deadlock), waits for process exit, then a short 2s drain window before returning captured output. A shared accumulator preserves partial output if the window expires.opencode/src/tool/shell.ts: after the direct child exits, drain briefly then interrupt the stream-reader fiber so the effect scope closes.tui/src/util/transcript.ts:/copynow renders captured output for aborted/error tool parts.Completion is now anchored to the direct process exiting instead of pipe EOF, so a surviving grandchild can't block the tool; the bounded drain still captures all in-flight output.
Related fix attempt #42275 only covers the first layer and explicitly leaves callers waiting for stream completion; this PR closes that gap. The background-shell features #40005 / #33310 are intentionally out of scope (managed background jobs remain explicitly opt-in).
How did you verify your code works?
bun typecheck: @opencode-ai/core, opencode, @opencode-ai/tui all pass.bun test: process (32), tool-bash, cross-spawn-spawner (incl. new regression:exitCoderesolves on direct-child exit while a descendant holds the pipe open), shell tool (66, incl. new regression: foreground exits but a descendant holds the pipe 6s → tool returns in < 8s), transcript (20, incl. aborted-tool output), processor-effect (17).--singlebuild passes the smoke test.running~6 min until abort, then ended aserror: "Tool execution aborted"with no output in/copy. The same command returns promptly in an interactive pwsh because stdout is a console handle, not a pipe.Screenshots / recordings
N/A — no UI change.
Checklist