aaif-goose / aaif-goose/goose

Claude ACP session stuck after cancelling a turn

Aperta
#11,651 1 commento 0 reazioni 1 assegnatario Rivendicata da @Abhijay007 Vedi su GitHub
Lingua principale
Rust
Stelle
54.2k
Fork
6.2k
Merge medio
3g 4h
PR unite (30g)
240

Descrizione

**Describe the bug**

A Berd user interrupts a session midturn to send another message, and the session silently stalls for as long
as the abandoned turn happens to run — 10 minutes in the reported case.
The affected internal user volunteered **the full chat session export**. Reach out to me for more info as I don't want to post that publicly.

What the user experienced:
- Agent was mid-turn, waiting on approval for a shell command.
- User typed a replacement message ("Insert real links. Just rewrite the entire doc.").
- Session went completely silent — no output, no tool calls, no error, no notification.
- Typed "What are you doing?" 3 minutes later — no reply either.
- ~10 minutes after the original message, output resumed and the task completed
normally, with no indication anything had gone wrong.
- Their summary: *"I didn't see any errors or notifications, you just sat there."*
- Only working remedy was restarting Berd — which kills the goosed sidecar and
the agent child, discarding in-flight work.

What the agent was doing during those 10 minutes (invisible to the user):
- Still executing the abandoned turn.
- Every approval-requiring tool failed instantly with
`Tool permission request failed: Error: Internal error`.
- Looped sleep-and-retry trying to make progress — which is what stretched the
stall to 10 minutes.

Cause: goose tears down its side of a cancelled/replaced turn but never sends
`session/cancel` to the ACP agent child (`crates/goose/src/acp/provider.rs`
contains no `CancelNotification` anywhere). The zombie turn keeps running, and:
- child `session/request_permission` → bare JSON-RPC **-32603 Internal error**
(`provider.rs:1389–1408`), unlogged. Claude Code renders it to the model as
the `Tool permission request failed` above.
- child `session/update` → silently dropped (`let _ = tx.try_send(...)`);
nothing persisted, nothing forwarded to the frontend.
- user's replacement prompt → queued invisibly; `handle_requests` is strictly
serial and stays parked awaiting the child's `session/prompt`
(`provider.rs:1697–1725`).
- no timeout or safeguard on this path — recovery is just the zombie turn
ending on its own, after which the queued prompt dispatches on a fresh channel.

Scope: provider-layer, so expected to affect every ACP-backed provider
(claude-acp, codex-acp, copilot-acp, amp-acp, pi-acp), on both the legacy agent
loop and the state-machine path.

---

**To Reproduce**

Manual (ACP frontend, claude-acp provider, `goose_mode: smart_approve`):
1. Ask for a task involving several shell commands (each triggers a permission
round-trip).
2. Submit a new chat message while a permission request is in flight. A
permission handler that delays ~2s before answering makes the window easy to hit.
3. Session goes silent: new message produces nothing, no error surfaces, the
goose session file stops recording.
4. Recovery only when the abandoned child turn ends on its own. Post-recovery,
the agent's transcript shows repeated
`Tool permission request failed: Error: Internal error`.

Deterministic (integration-style, fixture at `crates/goose/tests/acp_fixtures/server.rs`):
1. Connect an `AcpProvider` to a fixture agent; start prompt A; fixture issues
`session/request_permission` and holds the turn open.
2. Drop the returned `MessageStream` (simulates cancellation), then call
`stream()` again (prompt B).
3. Fixture sends another `session/request_permission` plus a `tool_call` update
within turn A.
4. Observe: no `session/cancel` ever arrives; the permission request gets
-32603; the update vanishes; prompt B waits until the fixture ends turn A.

---

**Expected behavior**

- Cancelling/replacing a turn ends the child's turn promptly via `session/cancel`
(conforming ACP agents finish with `StopReason::Cancelled`).
- An unanswerable permission request resolves as a clean cancellation the model
can reason about, not an opaque internal error.
- A prompt submitted after cancellation dispatches in seconds, not "whenever the
abandoned turn ends."
- These paths log something — today the -32603 branch and the dropped updates
are entirely silent, which is what made diagnosis hard.

---

**Screenshots**

Image

---

**Please provide the following information**
- **OS & Arch:** macOS arm64 `[confirm with reporter]`
- **Interface:** Neither goose UI nor CLI — goosed's ACP server over WebSocket,
driven by an internal desktop frontend (Berd). Bug is in goosed's ACP provider
layer; reproduces with any ACP client.
- **Version:** goose @ `b9b671c6` (pinned by the frontend). `crates/goose/src/acp/`
differs from current `main` by only 5 unrelated lines, so this reproduces on `main`.
- **Extensions enabled:** Not implicated — failure is below the extension layer.
`[exact list available from the session export if needed]`
- **Provider & Model:** claude-acp via `@agentclientprotocol/claude-agent-acp@0.66.0`
wrapping Claude Code; `goose_mode: smart_approve`. All ACP providers share the
affected path.

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

The bug is in the ACP provider layer, specifically in crates/goose/src/acp/provider.rs where a cancelled turn does not send a session/cancel notification. Start by examining the provider.rs file, focusing on the handle_requests function and the lack of a CancelNotification. Look at the integration test fixture at crates/goose/tests/acp_fixtures/server.rs to understand the deterministic reproduction steps. The fix involves implementing proper cancellation signaling and error handling to prevent silent stalls.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
rust
Ambito
ai-infra-agents, backend
Tipo di issue
Bug
Difficoltà
4/5
Tempo stimato
3-5 giorni
Stato di attività
Attiva
Chiarezza
Specificata chiaramente
Idoneità per principianti
45/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.