Skip to content

Conversation

@evalstate
Copy link
Owner

No description provided.

The issue was a race condition between streaming chunks and the final
message. When agent.send() completed, it would immediately send a final
sessionUpdate with the complete response. However, streaming chunk tasks
created via asyncio.create_task() were not awaited, so they could still
be in flight, causing the final message to arrive before or interleaved
with the last streaming chunk.

Solution:
1. Track all streaming tasks in a list as they're created
2. After agent.send() completes, await all streaming tasks with asyncio.gather()
3. Only send final message if streaming wasn't active (avoiding duplicates)

This ensures the final message is never sent before streaming completes,
and eliminates duplicate messages since the last streaming chunk already
contains the complete response.

Fixes duplicate message issue in ACP client connections.
When streaming is enabled, the final chunk already contains the complete
response text, so sending an additional final message causes duplicate
messages to be received by the client.

This fix introduces a `streaming_enabled` flag that tracks whether
streaming was set up for a prompt. When streaming is enabled, we skip
sending the redundant final message, preventing duplicates.

This approach is simpler than tracking and awaiting streaming tasks,
and eliminates the race condition between the final streaming chunk
and the final message.

Fixes issue where ACP clients received messages twice.
The issue was that streaming chunk tasks were created but not awaited
before returning PromptResponse(stopReason=END_TURN). This caused
chunks to potentially arrive AFTER the turn was marked complete,
leading to duplicate/out-of-order messages.

Solution:
1. Track all streaming tasks in a list as they're created
2. After agent.send() completes, await all tasks with asyncio.gather()
3. Skip final sessionUpdate if streaming was enabled (last chunk has complete response)
4. Then return PromptResponse with END_TURN

This ensures proper message ordering:
  All streaming chunks → (optional final message) → END_TURN

Fixes duplicate ACP messages issue.
The previous fix incorrectly checked streaming_enabled (whether a
listener was registered) instead of checking if chunks were actually
sent. This caused no notifications to be sent when streaming was
enabled but no chunks arrived (e.g., with passthrough model).

Now we check if streaming_tasks is empty:
- If chunks were streamed: skip final update (last chunk has everything)
- If no chunks were streamed: send final update (even if listener registered)

This fixes test failures where client received no sessionUpdate notifications.
@evalstate evalstate merged commit 704f420 into main Nov 9, 2025
6 checks passed
@evalstate evalstate deleted the claude/fix-duplicate-acp-messages-011CUy6TpaSgEh8qeVVJJ2SK branch November 27, 2025 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants