[BUG] claude-agent-acp: streaming loop only exits on session_state_changed: idle, leaving Zed stop button stuck after every turn
Affected package: @agentclientprotocol/claude-agent-acp (verified on v0.31.4 and v0.33.1)
Affected client: Zed (any recent version using the ACP integration)
Summary
The streaming loop in acp-agent.js exits the user-turn only on session_state_changed: { state: "idle" }. The result message — which is the actual semantic end of a Claude Code turn — falls through to a bare break; and the loop keeps waiting. When idle lags or never arrives, session.promptRunning stays true indefinitely.
Symptom (in Zed)
- Stop button stays active after Claude finishes responding.
- User has to click Stop manually before sending the next message (clicking Stop forces the stream closed, which finally lets the loop exit).
- Sound alert (Zed's
AcpThreadEvent::Stopped) is delayed by the same mechanism — it fires on stream close, not onresult.
Repro
- Use Zed with the Claude Code ACP agent.
- Send any prompt — especially one that launches background tasks.
- After Claude's response finishes streaming, observe that the stop button stays active in the chat input.
Root cause
In node_modules/@agentclientprotocol/claude-agent-acp/dist/acp-agent.js, case "result": ends with break; instead of returning. The only path that returns and closes the prompt is case "session_state_changed": when message.state === "idle". If idle never arrives, the loop hangs forever.
Proposed fix
At the end of case "result": in acp-agent.js, replace the trailing break; with:
For v0.33+ (which has the isTaskNotification flag for autonomous task-notification followups):
if (!isTaskNotification) {
return { stopReason, usage: sessionUsage(session) };
}
break;
The !isTaskNotification guard prevents an autonomous followup result from terminating the user-turn early.
For v0.31 and earlier (no isTaskNotification):
return { stopReason, usage: sessionUsage(session) };
result is the semantic end of the turn — the dependency on session_state_changed: idle is sloppy. Same return shape as the idle handler already uses.
Why this matters
Every Zed + Claude Code user hits this. It reads like a Zed bug to the user ("Zed's stop button is broken") but the fix lives in the Anthropic-published bridge.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗