fix: FIFO message queue — process queued messages sequentially after agent finishes#21891
Open
Mr-V1be wants to merge 2 commits intoanomalyco:devfrom
Open
fix: FIFO message queue — process queued messages sequentially after agent finishes#21891Mr-V1be wants to merge 2 commits intoanomalyco:devfrom
Mr-V1be wants to merge 2 commits intoanomalyco:devfrom
Conversation
…agent finishes Fixes anomalyco#2246, anomalyco#1476 — queued messages now process one at a time after the agent fully completes each turn, instead of being silently dropped or processed out of order. Root cause: Runner.ensureRunning() drops new work when state=Running, and the TUI generates MessageIDs at keypress time (not processing time), causing queue-delayed messages to appear at the wrong position in chat. Changes: - JS-level promise chain in static prompt() serializes calls in FIFO order - waitForIdle() in run-state.ts blocks until current agent run completes - createUserMessage always generates fresh MessageID at insertion time - runLoop continues checking for queued messages instead of breaking - System-reminder filter prevents queued messages from leaking into the current turn's LLM context - Cancel (Esc) respects user intent — doesn't auto-restart queue - Status bar shows "N queued" badge with click-to-view message list Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The test expected the old behavior where user messages are created immediately while the agent is busy. With FIFO queue, the second message waits for the first run to complete before being created. - Resolve gate before waiting (no deadlock) - Check parentID against the actual user message ID (not pre-generated) - Increase timeout to 10s for sequential processing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes #2246
Closes #1476
Type of change
What does this PR do?
When a user sends multiple messages while the agent is busy, they are silently dropped — only the last one might get a response. This PR fixes that by implementing a proper FIFO queue.
Root cause (3 issues):
Runner.ensureRunning()discards new work when state isRunning— returns the current run's deferred, never starts the queued workrunLoopbreaks entirely after model finishes (outcome === "break") instead of checking for more queued messagesMessageIDat keypress time, so queue-delayed messages sort at the wrong chronological position in chatFix:
prompt()function that serializes calls by session ID before they reach the Effect runtime. This guarantees FIFO order based on call order, not fiber scheduling ordercreateUserMessagenow always generates a freshMessageID.ascending()at insertion time instead of using the TUI-provided one, fixing the position bugwaitForIdle()toSessionRunState— blocks until the current agent run fully completes before creating the next messagerunLoop'sbreaktocontinueafter model finishes, so it re-checksactiveUserID()for more queued messagesloop()retry checks — prevents auto-restart after user cancellationI understand why each change is needed: the serialization must happen at the JS Promise level (not inside Effect fibers) because Effect-ts fiber scheduling doesn't guarantee execution order matches call order. The MessageID fix is needed because ULID-based IDs embed timestamps, and the TUI/sync layer sorts messages by ID.
How did you verify your code works?
Tested manually in the TUI:
queue.activeUserIDcorrectly identifies unhandled messages and processes them in orderScreenshots / recordings
N/A — terminal TUI changes, tested interactively
Checklist