FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Make Ctrl+C actually cancel tools: AbortSignal through bash, sub-agents, and walks by uuzzrm · Pull Request #2 · nullcache/corecoder-ts · GitHub

Make Ctrl+C actually cancel tools: AbortSignal through bash, sub-agents, and walks - #2

Merged
nullcache merged 1 commit into
nullcache:mainfrom
uuzzrm:fix/abort-propagation
Aug 17, 2026
Merged

Make Ctrl+C actually cancel tools: AbortSignal through bash, sub-agents, and walks#2
nullcache merged 1 commit into
nullcache:mainfrom
uuzzrm:fix/abort-propagation

Conversation

uuzzrm commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Why this is core

The abort story currently stops at the tool boundary. Ctrl+C aborts the LLM stream and the loop, but a running bash command (npm install, a test suite), a sub-agent, or a large directory walk ignores it — the agent waits for the tool to finish, so ^C appears broken exactly when users need it most. This PR makes cancellation flow from the agent, through every tool, down to the child process.

Changes

Tool.execute(args, signal?) — signal plumbing (src/tools/base.ts, src/agent.ts)

  • The Tool interface now accepts an optional AbortSignal; execTool passes the agent's signal into every tool.
  • execTool rethrows AbortError instead of stringifying it into a tool result. Previously a tool-level abort would have been fed back to the model as Error executing bash: AbortError and the turn would limp on.
  • After tool results are pushed, an abort that fired during execution ends the turn immediately (no wasted LLM round); pending replies are backfilled with [interrupted] as before.

bash — actually kill the command (src/tools/bash.ts)

  • Replaced exec with spawn so we own the process. shell: true makes the real command a grandchild of the shell; a plain child.kill() kills only the shell, and the command keeps running — and keeps holding the stdout pipe, so even exec's callback waits for it.
  • POSIX: detached: true makes the shell a process-group leader; kill(-pid) kills the whole group.
  • Windows: taskkill /pid <pid> /T /F walks the process tree (verified: a 10s node -e command now aborts in ~450ms instead of 10s).
  • Kept the 16MB output cap (now explicit, with a [output truncated] marker), and the timeout now kills the tree too.

agent (sub-agent tool) — propagate the signal (src/tools/agent.ts)

  • The parent's signal is passed into sub.chat(); an abort during the sub-agent's run propagates as AbortError (re-thrown, not stringified), so ^C cancels a runaway sub-agent too.

glob / grep — prompt walk cancellation

  • Both walks (up to 20k/5k entries) check the signal per iteration and on entry, so large-tree searches abort immediately instead of running to completion.

Tests (tests/abort.test.ts, 4 new)

  1. bash kills a running child on abort (<5s bound; measured ~450ms)
  2. agent aborts a running tool and backfills [interrupted]
  3. abort propagates through the sub-agent tool into the sub-agent's LLM call
  4. pre-aborted signals make glob and grep reject immediately

All 28 tests pass (npm test); demo still runs end-to-end.

nullcache merged commit 3f83d8c into nullcache:main Aug 17, 2026
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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL