ACP provider: cancelling a prompt doesn't interrupt the agent; UI stays locked until the turn finishes
- Lenguaje dominante
- Rust
- Estrellas
- 54.2k
- Forks
- 6.2k
- Merge medio
- 3 d 2 h
- PR fusionados (30 d)
- 262
Descripción
## Summary
When goose uses an ACP agent as its provider (e.g. Claude Code via the `claude_acp` provider), cancelling a prompt does not interrupt the agent. The session stays in the "running" state — the desktop **send button stays disabled** and you cannot even queue a message — until the agent finishes the entire turn on its own, which can take many minutes.
## Root cause
Two independent gaps in the ACP client provider (`crates/goose/src/acp/provider.rs`) and the agent reply loop (`crates/goose/src/agents/agent.rs`) combine:
**1. goose never sends `session/cancel` to the ACP agent.** The client→agent request channel has no cancel path:
```rust
enum ClientRequest { // provider.rs:65
NewSession { … }
SetMode { … }
SetConfigOption { … }
Prompt { … }
}
```
There is no `Cancel` variant and nowhere does the client send a `session/cancel` notification. (goose only *handles* incoming cancels when it is itself the ACP server, in `server.rs`.) `Provider::stream()` (provider.rs:472) does not even take a cancellation token. So when the user cancels, goose sets its own cancel token but the agent is never told to stop and keeps running the whole turn.
**2. goose's cancellation is cooperative and the check can't fire while the stream is blocked.** The reply loop only checks the cancel token *after* a stream item arrives:
```rust
while let Some(next) = stream.next().await { // agent.rs:2047
if is_token_cancelled(&cancel_token) || exit_chat { // agent.rs:2048
break;
}
```
There is no `tokio::select!` racing the token against the stream. The ACP provider stream yields only when the agent emits an update:
```rust
while let Some(update) = rx.recv().await { … } // provider.rs:522
```
and it only ends when `response_tx` is dropped — which happens after `block_task().await` on the outstanding `PromptRequest` completes (provider.rs:1168), i.e. only when the agent fully finishes the turn.
### Resulting chain
1. User cancels → cancel token set, but no `session/cancel` sent → agent keeps running.
2. While the agent is mid-turn with no output (slow tool call, thinking), `rx.recv().await` pends → `stream.next().await` pends.
3. The loop never reaches the `is_token_cancelled` check at agent.rs:2048, so the set token is never observed.
4. The `reply` future stays alive → session stays "running" → desktop keeps **send disabled**.
5. Only when the agent finishes on its own does the stream end and the UI free up.
## Repro
Use Claude Code as an ACP provider, send a prompt that triggers a long agentic turn, then cancel. The send button remains disabled for the remainder of the agent's turn.
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.