Watch
0
0
Fork
You've already forked hyperhive
0

subagents, ACP: stop with SIGINT only, so claude ends its own tool commands

claude's Bash tool starts every command in a new session, so a signal to
claude's process group never reaches it. claude stops those commands
itself when SIGINT makes it shut down. A SIGKILL leaves them running,
re-parented to init. Per the ruling on #4999, every stop is SIGINT and
nothing escalates.

- Subagent `interrupt` always sends SIGINT. The `force` argument is gone
  from the MCP tool and from `session::interrupt`. A caller that still
  sends `force: true` gets SIGINT; the argument is ignored.
- `interrupt` used to free the session name before it signalled, so a
  `continue` could start a second claude on the session while the first
  was still exiting. The `running` map now holds a three-state `Slot`
  (starting, running, stopping). An interrupted claude turn is
  `Stopping` until the turn task's own `finish_turn` clears it. `status`
  says "stopping", and `start`/`continue` on that name are refused.
  `interrupt` still returns at once. The ACP arm is unchanged: its name
  is freed when `session/cancel` is sent.
- ACP `Connection` drop sends its process group SIGINT, not SIGKILL, and
  the spawn no longer sets tokio's `kill_on_drop`, which SIGKILLed the
  leader after the `Drop` body. The test fixture's background grandchild
  resets SIGINT to default, since `&` starts it ignored.

Tests: a fake claude that records SIGINT, interrupted, fails with
`cancel(true)` (the old `force: true` path). An interrupted fake claude
that keeps running keeps its name claimed and `continue` is refused,
which fails when the slot is freed before the signal. A fake ACP agent
that records SIGINT, dropped, fails with SIGKILL in `Drop`, and also with
SIGINT in `Drop` but `kill_on_drop(true)` left in place.

Not changed: hive-claude's idle watchdog (still SIGKILL, separate
hive-claude change), the units' `KillSignal`, `machinectl kill`, and the
login helper.
This commit is contained in:
atlas 2026-10-10 19:52:25 +02:00 • committed by mara
commit 43e69d345e
7 changed files with 337 additions and 167 deletions

View file

@ -35,13 +35,15 @@ The crate depends on no hyperhive binary crate, so `hive-agent` and
A turn is stopped with `session/cancel`: by the `Canceller` handle, or by the
idle watchdog once no `session/update` has arrived for
`Config::idle_timeout`. An agent that has not answered the prompt 10s later
is killed, and the next turn respawns it. A watchdog stop fails the turn
with `AcpError::IdleTimeout` (`IdleKilled` if the agent was killed). This is
is sent SIGINT, and the next turn respawns it. A watchdog stop fails the turn
with `AcpError::IdleTimeout` (`IdleKilled` if the agent was sent SIGINT). This is
the only way a provider error the agent retries without reporting it, such as
an HTTP 429, ends the turn.
The agent leads its own process group. Killing it, and dropping the runtime,
`SIGKILL`s that group, so the commands the agent started die with it.
The agent leads its own process group. Stopping it, and dropping the runtime,
sends that group SIGINT, so the commands the agent started get it too.
Nothing escalates to SIGKILL: an agent that ignores SIGINT keeps running,
and the next turn starts a new one beside it.
## ACP backend: compaction