fix(acp): ignore idle cancel before next prompt - #50461
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for isolating the stale ACP interrupt path. The current-main premise is confirmed: acp_adapter/server.py:1221-1224 sets the cancel event and calls agent.interrupt() even while idle, while agent/conversation_loop.py:648-653 exits a new turn when that flag remains set.
Problems
tests/acp/test_server.py:342-347currently creates an idle session, invokescancel, and asserts thatcancel_eventis set. This PR intentionally clears that event for idle sessions, so the existing test will fail after applying the change.
Suggested changes
- Update
test_cancel_sets_eventto model a running session (state.is_running = True) before asserting the real-cancel behavior. The new idle-cancel regression test can then cover the opposite contract.
Automated hermes-sweeper review.
| # submitting the next prompt, even when the previous turn is | ||
| # already idle. Treat that as a no-op; otherwise a stale | ||
| # AIAgent interrupt poisons the next prompt and it returns | ||
| # interrupted_by_user without ever calling the model. |
There was a problem hiding this comment.
This intended idle no-op makes tests/acp/test_server.py:342-347 fail because that existing test cancels an idle session and asserts cancel_event.is_set(). Update that test to set state.is_running = True before asserting real cancellation behavior.
There was a problem hiding this comment.
Addressed in 517b579. The existing test now sets state.is_running = True (and is renamed to test_running_cancel_sets_event) before asserting the real-cancel behavior. The idle-cancel regression remains covered separately. Verified with both relevant test files: 88 passed.
|
Addressed the sweeper feedback in 517b579: Verification: |
Summary
cancelrequests received while a session is already idle as no-ops.Root cause
Some ACP clients, observed with Xcode, can send a best-effort
cancelimmediately before submitting the next prompt. Hermes previously always set the session cancel event and calledagent.interrupt()whenever a cancel request arrived, even ifstate.is_runningwas already false.That stale interrupt then poisoned the next
session/prompt:run_conversation()saw the pending interrupt before calling the model and returnedinterrupted_by_user. In Xcode this surfaced as:Fix
Only propagate cancel to the agent when the ACP session is actively running. If the session is idle, clear the cancel event and any stale agent interrupt instead.
This preserves real cancellation behavior for in-flight prompts while avoiding a stale idle cancel affecting the next user prompt.
Test plan