aaif-goose / aaif-goose/goose

ACP: agent updates that arrive outside a prompt are dropped — background-task turns never reach the user

Open
#11,320 0 comments 0 reactions 1 assignee Claimed by @alexhancock View on GitHub
Dominant language
Rust
Stars
54.2k
Forks
6.2k
Avg merge
3d 4h
Merged PRs (30d)
240

Description

**Describe the bug**

An ACP backend can send `session/update` notifications when no `session/prompt` is in flight. goose receives them and throws them away, so any work the backend does after the turn it was asked for is lost — silently, apart from a `WARN` line.

This is routine with `claude-acp`. Claude Code answers, ends the turn, and later resumes itself when a backgrounded task completes (a build, a test run) — producing a full turn of text and tool calls that the user asked for and never sees. The session simply stops at "waiting on the build" and nothing more arrives, so it reads as the model hanging.

The drop is in the notification dispatch handler (`crates/goose/src/acp/provider.rs`). `AcpProvider::stream` breaks out of its consumer loop at `AcpUpdate::Complete` and drops the receiver, so a chunk arriving afterwards fails to send and is discarded:

```rust
if let Err(e) = tx.send(AcpUpdate::Text(text)).await {
tracing::warn!(error = %e, "undelivered ACP agent message chunk");
}
```

There is no path in goose for an assistant message that is not a response to a prompt goose sent.

---

**To Reproduce**

1. Configure the `claude-acp` provider and start a session.
2. Ask for something that makes Claude Code start a long task in the background and end its turn — e.g. "kick off a release build in the background and tell me when it finishes".
3. The turn ends with a message like "I'll report when it finishes".
4. When the build finishes, Claude Code produces its report. Nothing appears in goose.
5. `~/.local/state/goose/logs/cli//*.log` shows one `undelivered ACP agent message chunk` warning per lost chunk.

The backend's own transcript (`~/.claude/projects//*.jsonl`) contains the complete lost turn, which is how I confirmed the content exists and only goose discards it.

---

**Expected behavior**

Updates that arrive for the active session outside a prompt are surfaced as a new assistant message: persisted to the session and pushed to the UI, so the session shows the late turn as it arrives.

`set_session_title_callback` is the existing precedent for a provider pushing something into the session outside the reply stream — the same shape would work here:

- a `Provider` hook alongside `set_session_title_callback` in `crates/goose-provider-types/src/base.rs`
- the ACP provider routing `Text`/`Thought`/tool-call updates to it when no turn is live, instead of warning and dropping
- `Agent::update_provider` persisting the message and emitting it on the session update channel
- a notifier in `crates/goose/src/acp/server.rs` alongside `spawn_session_name_update_notifier` to send `agent_message_chunk` to Desktop

Whatever the mechanism, the requirement is that goose stop discarding content it has already received.

---

**Please provide the following information**
- **OS & Arch:** macOS 26.6.1 arm64
- **Interface:** UI (Desktop)
- **Version:** v2.0-rc-04-27-0 (desktop 1.45.0)
- **Extensions enabled:** developer, context7, playwright, goose docs
- **Provider & Model:** claude-acp — claude-agent-acp 0.63.0 — claude-opus-5

---

**Additional context**

Evidence from a diagnostics bundle for one affected session: the goose transcript's last message is `"Waiting on just make-ui ... I'll report as soon as it lands."`, and the log carries twelve `undelivered ACP agent message chunk` warnings over the following six seconds — the whole report turn, dropped chunk by chunk.

The warning itself only exists because of the fix for #11178, which keeps `prompt_response_tx` registered past the prompt response; before that the drop was completely silent. That fix is about chunks racing the *end* of a turn, and its bounded grace window is deliberately short. This is the separate case of chunks belonging to no turn at all, which no grace window can cover.

Related but distinct: #10433 (a dropped MCP `notifications/tools/list_changed`) is the same class of defect — a notification goose receives and discards — on a different channel.

Contributor guide

Open the contributing guide

Research direction

The bug is in `crates/goose/src/acp/provider.rs` where the `AcpProvider::stream` loop discards updates after a turn completes. Examine the notification dispatch handler and the `tx.send` warning. Look at `crates/goose-provider-types/src/base.rs` for the `Provider` hook pattern similar to `set_session_title_callback`. Review `crates/goose/src/acp/server.rs` for `spawn_session_name_update_notifier` as a model for a new notifier. The fix involves routing `Text`/`Thought`/tool-call updates to a new hook when no turn is active, persisting the message via `Agent::update_provider`, and emitting it on the session update channel. Test by reproducing with `claude-acp` and checking logs for the warning.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
ai-infra-agents, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.