aaif-goose / aaif-goose/goose

Claude ACP session stuck after cancelling a turn

Đang mở
#11,651 1 bình luận 0 reaction 1 người được giao Được @Abhijay007 nhận Xem trên GitHub
Ngôn ngữ chính
Rust
Star
54.2k
Fork
6.2k
Merge trung bình
3 ngày 2 giờ
Pull request đã merge (30 ngày)
262

Mô tả

**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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.