Fixes #11613. ## Problem The bash tool waits for stdout/stderr to close, not only for the shell to exit (#5303). A background process that inherited those pipes and keeps writing keeps the tool waiting after the shell exited. When the timeout then hits, the tool reported only `Command timed out after N seconds`. Example, timeout 3s: ```bash bash -c 'while true; do echo tick; sleep 0.05; done' & sleep 1; echo boom >&2; exit 1 ``` The shell exits with code 1 after ~1s. The tool still times out at the deadline, because the loop is still writing to its output. Both facts are true: the command failed with code 1, and something it started was still running at the timeout. The old message dropped the first, so the model read a failed command as a slow one and retried with a longer timeout, which never helps here. ## Fix The timeout stays a timeout, since the tool did hit its deadline while background processes were still writing. When the shell had already exited before the deadline, the message now also says so: ``` Command timed out after 3 seconds. The shell exited with code 1 after 1.1 seconds, but background processes it started kept writing output until the timeout. ``` A shell still running at the deadline keeps the plain `Command timed out after N seconds`. The local backend throws a module-private `ShellTimeoutError` with the message `timeout:<seconds>` as before, so custom `BashOperations` that throw `timeout:N` are unaffected. ## Behavior per platform - Linux and macOS: the shell runs in its own process group (`detached: true`). At the deadline `kill(-pgid)` still reaches the background processes after the shell exited, the pipes close, and the tool returns at the deadline. Tested on Linux; macOS uses the same mechanism and matches the macOS repros in the issue. - Windows: still not fully solved. At the deadline `taskkill /T /PID <shell>` fails with `process not found`, because the shell pid is gone. Walking parent pids does not help: Git's `bin\bash.exe` is a launcher for `usr\bin\bash.exe`, and the orphaned loop's parent is that already-exited inner bash, not the pid pi holds. The tool keeps reading until the output pauses for 100ms. Measured with Git Bash: the message is now correct, but the tool returned after 14s instead of 3s; with a loop that never pauses it never returns. This PR only fixes the reporting. ## Same pattern elsewhere - `packages/durable/src/env/node.ts` (`NodeExecutionEnv.exec`): same code, same false `timeout:N` after the shell exited. Same Windows limitation. - `packages/coding-agent/src/core/exec.ts` (`pi.exec` for extensions): the timeout sends SIGTERM/SIGKILL to the direct child only, no process group. A descendant that keeps writing keeps the call waiting past the timeout on every platform, and the result has `killed: true` even when the child had already exited with its own code. - `packages/coding-agent/src/core/package-manager.ts` (capture commands for git/npm): waits for `close` and kills only the direct child on timeout, so a descendant holding the pipes makes the timeout ineffective on every platform, even when the descendant writes nothing. - `packages/mcp/src/transports/stdio.ts`: on Windows, closing a server that already exited skips taskkill, so children holding its pipes survive. The code documents this as a known limitation. Possible follow-up for Windows and the cases above: once the deadline passed, stop waiting for the pipes (destroy our ends). On Windows this also ends most writers, which die from a broken pipe (tested with Git Bash). ## Tests - `test/suite/regressions/11613-bash-timeout-after-shell-exit.test.ts` (Unix only; it would hang on Windows for the reason above): background writer after the shell exits with code 3, and a shell still running at the deadline. The first case fails without the fix. - Checked manually on Windows 11 with Git Bash through the built bundle: both messages are as above. - `npm run check`, `test/tools.test.ts`, the #5303 and #5208 regressions, `bash-close-hang-windows`, `powershell-tool`, `shell-kill-process-tree`. This PR description is AI-generated. |
||
|---|---|---|
| .. | ||
| docs | ||
| examples | ||
| install-lock | ||
| schemas | ||
| scripts | ||
| src | ||
| test | ||
| .gitignore | ||
| CHANGELOG.md | ||
| package.json | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.examples.json | ||
| vitest.config.ts | ||
New issues and PRs from new contributors are closed automatically. Maintainers review closed submissions daily. See CONTRIBUTING.md.
Pi
Pi is a minimal, extensible agent harness that you can make your own.
Adapt Pi to your workflows, not the other way around. Customize Pi with extensions, skills, prompt templates, and themes. Bundle them as Pi packages and share via npm or git.
Pi ships with powerful defaults but skips features like sub-agents and plan mode. Ask Pi to build what you want, or install a package that does it your way.
Use Pi interactively, automate it in print or JSON mode, control it over RPC, or build apps with the Pi TypeScript SDK. See OpenClaw for a real-world integration.
Getting started
Install the command-line interface:
curl -fsSL https://pi.dev/install.sh | sh
On Windows:
powershell -c "irm https://pi.dev/install.ps1 | iex"
The installer pins all dependencies and updates Pi with pi update. Alternatively, install directly with npm, which does not pin transitive dependencies:
npm install -g --ignore-scripts @earendil-works/pi-coding-agent
Pi requires Node.js 22.19 or newer. The macOS, Linux, and Windows installers can install it if needed. Pi does not require dependency lifecycle scripts for a normal npm installation.
On macOS and Linux, Nix users can install the latest release with nix profile add github:earendil-works/pi/stable. See the quickstart for updating and pinning releases.
Start Pi in the directory where you want it to work:
cd /path/to/project
pi
For a built-in AI provider, run /login inside Pi to connect a subscription or API key. Then give Pi a task.
See the documentation for full setup and usage instructions.
Share your OSS coding agent sessions
If you use Pi for open source work, please share your coding agent sessions.
Public OSS session data helps improve models, prompts, tools, and evaluations using real development workflows.
For the full explanation, see this post on X.
To publish sessions, use badlogic/pi-share-hf using a Hugging Face account and the Hugging Face CLI.
- Demo video on how to publish Pi sessions
- Published Pi development sessions:
badlogicgames/pi-monoon Hugging Face.
Development
Clone the repository, install its dependencies, and run Pi from source:
git clone https://github.com/earendil-works/pi
cd pi
npm install --ignore-scripts
./pi-test.sh
pi-test.sh can be called from any directory and preserves the caller's working directory.
Before submitting changes, run:
npm run check
./test.sh
Read CONTRIBUTING.md before opening an issue or pull request. It defines the contribution gate, issue quality bar, and required checks. Read AGENTS.md for repository-specific implementation, testing, dependency, and release rules.
License
MIT