Skip to content

fix(claude): flush OutgoingMessageQueue before consuming next user turn#909

Open
swear01 wants to merge 1 commit into
tiann:mainfrom
swear01:fix/claude-agent-messages-sort-below-next-turn
Open

fix(claude): flush OutgoingMessageQueue before consuming next user turn#909
swear01 wants to merge 1 commit into
tiann:mainfrom
swear01:fix/claude-agent-messages-sort-below-next-turn

Conversation

@swear01

@swear01 swear01 commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

  • OutgoingMessageQueue.scheduleProcessing() defers socket.emit() via setTimeout(fn, 0) (macrotask)
  • The Claude SDK's nextMessage() callback runs in a microtask chain — before that setTimeout fires
  • This allows messages-consumed for turn N+1 to reach the hub before the queued agent messages from turn N, causing the hub to stamp invokedAt_N+1 earlier than those agent messages' created_at
  • Since compareMessages sorts ascending by invokedAt ?? createdAt, the late-stamped agent messages sort permanently below the N+1 user message

Fix: await messageQueue.flush() at the top of nextMessage() so all pending outgoing agent messages are sent through the socket before messages-consumed is dispatched.

Test plan

  • Multi-turn Claude session with tool use: agent messages from turn N should appear above the turn N+1 user message
  • Single-turn Claude session: no regression in message ordering
  • Sessions with plan mode / abort: no messages dropped or duplicated

Closes #908

🤖 Generated with Claude Code

OutgoingMessageQueue.scheduleProcessing() defers socket.emit() via
setTimeout(fn,0) — a macrotask. The Claude SDK's nextMessage() callback
runs in a microtask chain, which executes before that macrotask fires.

This means messages-consumed for turn N+1 can be sent to the hub before
the queued agent messages from turn N have been emitted. The hub stamps
invokedAt on the N+1 user message at receive time, and then stores the
late-arriving agent messages with created_at > invokedAt_N+1. Since
compareMessages sorts by invokedAt ?? createdAt ascending, those agent
messages sort permanently below the N+1 user message.

Fix: await messageQueue.flush() at the top of nextMessage() so all
pending outgoing agent messages are sent through the socket before
messages-consumed is dispatched.

Closes tiann#908

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings
No issues found in the modified lines.

Summary
Review mode: initial
Reviewed the full latest diff for cli/src/claude/claudeRemoteLauncher.ts. The new messageQueue.flush() before consuming the next user turn matches the existing queue semantics: it releases pending delayed assistant messages and preserves send order before messages-consumed can stamp the next user message. Residual risk: this turn-boundary ordering behavior is not covered by an integration/regression test.

Testing
Not run (automation): bun is not installed in this runner environment, so bun test cli/src/claude/utils/OutgoingMessageQueue.test.ts failed before execution.

HAPI Bot

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.

bug(claude): agent messages from current turn sort permanently below next user message

1 participant