Skip to content

feat: add active-turn message delivery controls - #5396

Open
ClapFy wants to merge 2 commits into
pingdotgg:mainfrom
ClapFy:agent/active-turn-message-controls
Open

feat: add active-turn message delivery controls#5396
ClapFy wants to merge 2 commits into
pingdotgg:mainfrom
ClapFy:agent/active-turn-message-controls

Conversation

@ClapFy

@ClapFy ClapFy commented Aug 5, 2026

Copy link
Copy Markdown

What Changed

Adds a Messages while working setting to web and mobile with two delivery behaviors:

  • Steer (the default) sends the message into the active turn.
  • Queue stores follow-ups per thread and sends them FIFO, one turn at a time, after the current turn finishes.

The running web composer now keeps Stop generation visible beside the mode-specific send action. Mobile uses the same preference and snapshots it onto each outbox entry so existing queued work keeps its intended delivery behavior.

Codex sessions now use turn/steer when a turn is active, matching the steering behavior already supported by the other provider adapters. A successful steer reaffirms the running turn to orchestration so no stale pending-turn projection is left behind.

Why

While an agent was working, the composer only exposed Stop and did not let users choose whether a follow-up should affect the current turn or wait for the next one. That made rapid follow-ups awkward and left queue semantics inconsistent between web and mobile.

Delivery safeguards

  • Queues are durable per thread and preserve FIFO order without an artificial message cap.
  • Stable message and command IDs make retries idempotent.
  • Only one dispatcher may own a queued web message at a time.
  • The next queued message pauses after a non-interruption send failure and requires an explicit retry.
  • Mobile retains its bounded exponential retry delay and sends only the head of each thread queue.
  • Queue drain waits for a ready, connected thread, preventing overlapping turns and token-burning retry loops.

Verification

  • vp test run packages/contracts/src/settings.test.ts apps/mobile/src/state/thread-outbox.test.ts apps/server/src/provider/Layers/CodexSessionRuntime.test.ts apps/server/src/provider/Layers/CodexAdapter.test.ts apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts apps/web/src/webThreadOutbox.test.ts apps/web/src/components/settings/settingsSearch.test.ts — 137 tests passed
  • vp lint across all 23 changed TypeScript files with --report-unused-disable-directives
  • vp fmt across all 23 changed TypeScript files
  • Package type checks passed for contracts, web, mobile, and server
  • Isolated T3 Code web environment:
    • Queue mode drained two follow-ups FIFO exactly once and cleared persisted outbox storage
    • Steer mode injected the follow-up into the active Codex turn exactly once
    • Running composer exposed Stop beside Queue/Steer
  • T3 Code Dev on iOS Simulator: Queue and Steer flows verified
  • git diff --check

Model: GPT-5.6 Sol | Harness: Codex in T3 Code

Note

Add active-turn message delivery controls with 'steer' and 'queue' modes

  • Introduces ActiveTurnMessageBehavior ('steer' or 'queue', default 'steer') to contracts, client settings, and mobile preferences, controlling what happens when a user sends a message while a thread is actively running.
  • In 'steer' mode, the message is sent directly into the active Codex turn via a new turn/steer API call; in 'queue' mode, the message is held in a local outbox and delivered once the thread is idle.
  • Adds a persistent web thread outbox (webThreadOutbox.ts) with per-thread FIFO queues, cross-tab localStorage sync, deduplication, and pause/retry controls; mobile outbox gains a matching activeTurnMessageBehavior field on queued messages.
  • The web composer (ChatComposer.tsx) and mobile composer (ThreadComposer.tsx) update their primary action labels and enabled state based on the current behavior and phase ('Queue message', 'Steer active turn', or 'Send message').
  • Settings UIs on both web (SettingsPanels.tsx) and mobile (SettingsRouteScreen.tsx) expose a 'Messages while working' control that persists the user's choice.
  • Risk: sends during an active turn now have a brief async delay on mobile while awaitActiveTurnMessageBehavior waits for preferences to settle before enqueuing.

Macroscope summarized 56e5d17.


Note

Medium Risk
Changes orchestration-facing Codex turn start/steer paths and multi-client message delivery timing; mistakes could duplicate turns, drop queued messages, or mis-steer active work, though stable command IDs and explicit deferral logic mitigate this.

Overview
Adds activeTurnMessageBehavior (steer | queue, default steer) to contracts and surfaces it as Messages while working in web and mobile settings.

Steer sends follow-ups into the running turn; queue holds per-thread FIFO outboxes until the turn is idle. Web gets a new webThreadOutbox store (localStorage, cross-tab merge, single-dispatcher guard, pause/retry on failure). Mobile persists the preference, waits for preferences to settle before send, snapshots behavior on each outbox entry, and only defers delivery while busy when behavior is queue (or omitted on legacy entries).

While a turn is running, web and mobile composers show Stop alongside a send action labeled Steer or Queue instead of blocking send entirely. Web drains the outbox when the thread is ready and shows a queued-count row with Retry when delivery is paused.

Codex turn/steer is used when the session is already running; a synthetic turn/started reaffirms the active turn so orchestration projection does not leave a stale pending turn. Tests cover outbox rules, Codex steer mapping, and steer reconciliation in the projection pipeline.

Reviewed by Cursor Bugbot for commit 56e5d17. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d83c4c7-3f50-4d35-8bd9-d2fac8851f66

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XL 500-999 changed lines (additions + deletions). labels Aug 5, 2026
Comment thread apps/mobile/src/state/use-thread-composer-state.ts
Comment thread apps/mobile/src/state/use-thread-outbox-drain.ts
Comment thread apps/web/src/webThreadOutbox.ts
@ClapFy
ClapFy marked this pull request as ready for review August 5, 2026 10:43

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 56e5d17. Configure here.

isSendBusy,
isServerThread,
phase,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Web send ignores settings hydration

Medium Severity

The ChatView's message queuing logic accesses settings.activeTurnMessageBehavior before localStorage hydration. This can cause the first message sent after a page load to incorrectly use the default steer behavior instead of a user's queue preference.

Fix in Cursor Fix in Web

Triggered by learned rule: Feature flag hooks with per-stage defaults must gate on settings hydration

Reviewed by Cursor Bugbot for commit 56e5d17. Configure here.

session: Pick<ProviderSession, "status" | "activeTurnId">,
): TurnId | undefined {
return session.status === "running" ? session.activeTurnId : undefined;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Codex steer skipped while starting

Medium Severity

Mobile outbox delivery treats orchestration starting as busy but still sends when the queued snapshot is steer. resolveCodexSteeringTurnId only steers when the provider session is running, so follow-ups during starting fall through to turn/start instead of turn/steer, conflicting with steer semantics and projection reaffirm logic.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 56e5d17. Configure here.

(input.hasQueuedMessages ||
(input.activeTurnMessageBehavior === "queue" &&
(input.phase === "running" || input.isSendBusy)))
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Queue mode ignores starting session

Medium Severity

In queue mode, shouldQueueWebThreadMessage only treats phase === "running" (or local send busy) as active work. derivePhase maps session.status === "starting" to connecting, so after the first turn is acknowledged but the session is still starting, a follow-up can bypass the outbox and hit startThreadTurn directly instead of queuing.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 56e5d17. Configure here.

@macroscopeapp

macroscopeapp Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a significant new feature (active-turn message delivery controls) with cross-platform changes to mobile, web, and server, including new state management and API integration. Three unresolved review comments identify medium-severity bugs in the core feature logic.

You can customize Macroscope's approvability policy. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant