feat(web): add configurable queue/steer follow-up behavior#1479
feat(web): add configurable queue/steer follow-up behavior#1479leonardoxr wants to merge 19 commits intopingdotgg:mainfrom
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
I want this behavior to be server side. I shouldn't need to keep my client open for the queing to work |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 11ec9d73f3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
@juliusmarminge A question about the Steer behavior. Should the steer wait for the AI to finish its "step/thinking/action" before sending(like claude code does) or should it interrupt indeed. Or, should it also be an option? Like Queue / Steer / "Force Steer" |
|
Follow-up update on this PR after the review and manual testing: I moved queued follow-ups out of the client draft store and into server-owned orchestration state. What changed:
I also fixed two regressions uncovered while testing:
Coverage added/updated for:
Validation I ran before pushing this update:
|
|
Addressed the remaining review items in this branch:
Validation rerun after these fixes:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 775ee4286c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cace8fccce
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 06d68cdfaf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 46f95dac9d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d92e28bc8e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f049043dd4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…' into feat/follow-up-behavior-1462
apps/server/src/orchestration/Services/QueuedFollowUpReactor.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8162f8644e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 711f86d0eb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6f39aeb640
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }), | ||
| ); | ||
|
|
||
| if (Exit.isFailure(removeExit)) { |
There was a problem hiding this comment.
🟡 Medium Layers/QueuedFollowUpReactor.ts:203
When thread.queued-follow-up.remove dispatch fails but the follow-up send-failed dispatch succeeds, the in-memory blockedQueuedFollowUpIdsByThreadId is only set when the send-failed dispatch itself fails (lines 98-107, 185, 224). If the send-failed succeeds but the remove failed, the follow-up remains in the queue with lastSendError set, but processThread will still attempt to re-dispatch on the next event because canDispatchQueuedFollowUp checks the in-memory blockedQueuedFollowUpIdsByThreadId first (line 61-70) and only falls back to checking queuedHead.lastSendError if the in-memory block is absent. Since the send-failed event may not yet be projected when processThread re-runs, queuedHead.lastSendError could still be null, allowing a duplicate send.
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/orchestration/Layers/QueuedFollowUpReactor.ts around line 203:
When `thread.queued-follow-up.remove` dispatch fails but the follow-up `send-failed` dispatch succeeds, the in-memory `blockedQueuedFollowUpIdsByThreadId` is only set when the `send-failed` dispatch itself fails (lines 98-107, 185, 224). If the `send-failed` succeeds but the `remove` failed, the follow-up remains in the queue with `lastSendError` set, but `processThread` will still attempt to re-dispatch on the next event because `canDispatchQueuedFollowUp` checks the in-memory `blockedQueuedFollowUpIdsByThreadId` first (line 61-70) and only falls back to checking `queuedHead.lastSendError` if the in-memory block is absent. Since the `send-failed` event may not yet be projected when `processThread` re-runs, `queuedHead.lastSendError` could still be `null`, allowing a duplicate send.
Evidence trail:
apps/server/src/orchestration/Layers/QueuedFollowUpReactor.ts lines 61-70 (blockedQueuedFollowUpIdsByThreadId check), lines 79-87 (canDispatchQueuedFollowUp call with queuedHeadHasError), lines 185-226 (removeExit failure handling where blockedQueuedFollowUpIdsByThreadId is only set in catchCause at lines 214-223), lines 248-256 (event stream triggers processThread). packages/shared/src/orchestration.ts lines 232-252 (canDispatchQueuedFollowUp function returns false when queuedHeadHasError is true). Commit: REVIEWED_COMMIT
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| (entry) => entry.id === threadId && entry.deletedAt === null, | ||
| ); | ||
| return thread ? collectPersistedAttachmentIdsForThread(thread) : new Set<string>(); | ||
| }); |
There was a problem hiding this comment.
Full DB snapshot loaded for single-thread attachment validation
Medium Severity
listThreadPersistedAttachmentIds calls projectionReadModelQuery.getSnapshot(), which executes a full transactional read of every projection table (projects, threads, messages, activities, sessions, queued follow-ups, checkpoints, turns, state) just to collect image attachment IDs for a single thread. The in-memory read model via orchestrationEngine.getReadModel() — already resolved in the same scope on line 337 — provides the same data without any database I/O. This runs on the command dispatch hot path for every thread.turn.start, thread.queued-follow-up.enqueue, or thread.queued-follow-up.update that references a previously persisted attachment.


Closes #1462
Summary
Adds explicit follow-up behavior while a thread is already running.
Users can now choose a global
Follow-up behaviorsetting:Steer: send the follow-up as guidance for the active runQueue: hold the follow-up and auto-send it after the current run settlesThe composer also supports a one-off opposite behavior shortcut for a single message.
What changed
followUpBehaviorto client settings with a default ofsteerQueuevsSteerCtrl+Shift+EnterCmd+Shift+EnterWhy
This makes follow-up delivery explicit and predictable while a run is active, which is the core problem described in #1462.
Verification
bun fmtbun lintbun typecheckbun run test -- src/composerDraftStore.test.tsbun run test:browser -- src/components/ChatView.browser.tsx --testNamePattern "queued|follow-up"Media
Settings
Before
After
Before/after comparison
Preview
Note
Medium Risk
Adds new persisted queued-follow-up state plus an always-on reactor that can auto-dispatch turns, touching core orchestration/projection paths and attachment validation; regressions could lead to unexpected sends or orphaned files.
Overview
Adds first-class queued follow-ups to orchestration: new
thread.queued-follow-up.*commands/events are projected into a new persisted queue table and surfaced in snapshots asthread.queuedFollowUps.Introduces a new
QueuedFollowUpReactorthat watches thread events/read-model state and automatically dispatches the queue head when the thread becomes sendable (including applying queued runtime/interaction modes and persisting send failures).Extends attachment handling to queued follow-ups: the projection pipeline now prunes queued-follow-up attachment files on queue mutations/reverts, and
wsServernormalizes queued follow-up attachments (persist data URLs, and validate persisted attachment IDs are thread-scoped). Integration/unit tests were updated/added to cover restart replay, snapshot hydration, queue projections, and attachment pruning/validation.Written by Cursor Bugbot for commit a2754fa. This will update automatically on new commits. Configure here.
Note
Add configurable queue/steer follow-up behavior for chat turns
FollowUpBehaviorsetting ('queue' or 'steer', default 'steer') in client settings and the General Settings panel, controlling what happens when a user submits a follow-up while a turn is running.ComposerQueuedFollowUpsPanelUI; a platform-aware keyboard shortcut (Cmd/Ctrl+Shift+Enter) inverts the configured behavior inline.QueuedFollowUpReactorbackground worker that auto-dispatches the head queued follow-up when the thread is ready, handling send errors and blocking on failure.projection_thread_queued_follow_upstable (migration 019) with full attachment, terminal context, and model selection storage; adds aqueuedFollowUpsarray to the orchestration read model and snapshot.COMPOSER_DRAFT_STORAGE_VERSIONto 4, which will invalidate previously persisted composer draft state.Macroscope summarized a2754fa.