claude runtime: interrupting a session kills every process it started
hive-claude 0.1.3 spawns claude as the leader of its own process group, and its `Cancel::cancel` and idle watchdog signal that group instead of claude's pid alone. Commands claude started (a `nix build`, a test run) used to survive a subagent `interrupt` and keep running. Bump hive-claude to 0.1.3. The subagent `interrupt` call site needs no code change: `Cancel::cancel(force)` kept its signature. The harness's `/cancel` and `/logout` did not go through hive-claude: they ran `kill -INT <pid>` on the claude child they found in /proc. They now SIGINT the group that child leads, through `libc::kill`. A `claude` child that leads no group (the `claude plugin` calls) shares the harness's group and is still signalled alone. Tests: an interrupted subagent's grandchild dies, for both SIGINT and `force`, and fails on hive-claude 0.1.2; a harness cancel stops a turn's grandchild, and fails when only the pid is signalled.
This commit is contained in:
parent
63ab0bbd21
commit
028c283ff9
8 changed files with 244 additions and 24 deletions
|
|
@ -166,6 +166,11 @@ from how the child exited. `continue` clears it and hands the session a
|
|||
fresh turn allowance, which is what makes an interrupt a pause you can
|
||||
undo rather than a session you have to abandon.
|
||||
|
||||
On `claude`, the signal goes to claude's whole process group, so the
|
||||
commands the subagent started (a `nix build`, a test run) get it too. The
|
||||
signal doesn't reach a command that moved itself out of the group
|
||||
(`setsid`).
|
||||
|
||||
When the session knows where its report goes — `start`'s `report_file`,
|
||||
or the path the subagent names when it signals — the daemon appends the
|
||||
stop reason to that file as well, so the artifact you were going to read
|
||||
|
|
|
|||
|
|
@ -227,11 +227,11 @@ Slash commands today:
|
|||
|
||||
- `/help` — list commands locally.
|
||||
- `/clear` — wipe the local terminal view (server history kept).
|
||||
- `/cancel` — `POST /api/cancel` → host shellouts `pkill -INT
|
||||
claude`, emits a Note. On an ACP agent it sends the agent
|
||||
`session/cancel` instead, and kills it if the turn hasn't ended 10s
|
||||
later. Also surfaces as a `■ cancel turn` button in the state row
|
||||
while state=thinking.
|
||||
- `/cancel` — `POST /api/cancel` → SIGINT to the turn's claude and every
|
||||
command it started (its process group), emits a Note. On an ACP agent
|
||||
it sends the agent `session/cancel` instead, and kills it if the turn
|
||||
hasn't ended 10s later. Also surfaces as a `■ cancel turn` button in
|
||||
the state row while state=thinking.
|
||||
- `/compact` — `POST /api/compact` → sets the deferred
|
||||
`Bus::request_compact()` flag and returns immediately. The harness
|
||||
runs the `/compact` at the next turn boundary (end of the in-flight
|
||||
|
|
@ -272,8 +272,8 @@ shaped).
|
|||
- `POST /login/{start,code,cancel}` — claude OAuth login flow.
|
||||
Start/cancel emit `LiveEvent::StatusChanged` to flip the
|
||||
badge to/from `needs_login_in_progress`.
|
||||
- `POST /api/cancel` — SIGINT the in-flight claude turn. Emits a
|
||||
`LiveEvent::Note`.
|
||||
- `POST /api/cancel` — SIGINT the in-flight claude turn and the
|
||||
commands it started. Emits a `LiveEvent::Note`.
|
||||
- `POST /api/compact` — run `/compact` on the persistent session
|
||||
(same MCP config + system prompt + allowed tools as a normal
|
||||
turn — only the stdin payload differs). Flips state to
|
||||
|
|
|
|||
Loading…
Reference in a new issue