aaif-goose / aaif-goose/goose

ACP provider: cancelling a prompt doesn't interrupt the agent; UI stays locked until the turn finishes

Abierto
#10,619 5 comentarios 0 reacciones 1 asignado Reclamado por @lifeizhou-ap Ver en GitHub
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

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.