Skip to content

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

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

feat: add active-turn message delivery controls#5396
ClapFy wants to merge 8 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 to steer or queue messages while a turn is running

  • Introduces ActiveTurnMessageBehavior ('steer' or 'queue', default 'steer') to ClientSettings, mobile preferences, and the server-side QueuedThreadMessage schema, allowing users to configure what happens when they send a message while a session is active.
  • On web, ChatView enqueues messages to a new per-thread useWebThreadOutboxStore (cross-tab persistent via localStorage) when queuing is required; WebThreadOutboxDrain drains the queue in FIFO order, updating thread settings and pausing on definitive failures.
  • On mobile, useThreadComposerState awaits persisted preferences before sending, clears the draft immediately, includes activeTurnMessageBehavior in the queued payload, and restores the draft on failure.
  • The Codex session runtime's sendTurn now attempts a turn/steer when the session is running, falls back to turn/start if steering is rejected, and serializes concurrent calls with a semaphore.
  • The send button and composer labels update dynamically: 'Steer active turn' or 'Queue message' while running; a queued message count row with a Retry button appears when messages are pending.
  • Risk: localStorage-backed outbox introduces cross-tab state synchronization; environments without localStorage fall back to volatile in-memory storage with a warning toast.

Macroscope summarized 57d23c5.


Note

High Risk
Changes message delivery, Codex session/orchestration reconciliation, and durable outbox state across web and mobile; mis-steering or queue drain bugs could duplicate turns or drop user messages.

Overview
Introduces activeTurnMessageBehavior (steer | queue, default steer) in shared settings and on mobile preferences, with Messages while working controls in web and mobile settings.

Steer injects follow-ups into the running turn; queue holds them per thread and delivers FIFO after the current turn. The running composer now shows Stop next to a mode-specific primary action (Steer / Queue / Send) and surfaces queued counts with retry when delivery pauses.

Web adds a durable useWebThreadOutboxStore (localStorage, cross-tab) and WebThreadOutboxDrain to send queued heads when the thread is ready; ChatView enqueues instead of blocking when queue mode (or an existing queue) applies, and restoreComposerContent merges failed sends without clobbering text typed after send.

Mobile snapshots behavior on each outbox entry, awaits settled preferences before enqueue (avoiding a steer default while prefs load), and updates outbox drain so steer can send while a turn is running and steerable.

Server (Codex) routes active-turn sends through turn/steer (semaphore-serialized, fallback to turn/start on rejection), synthesizes turn/started when steer keeps the same turn, maps session/error, and adds projection coverage so steers reconcile to a single running turn.

Reviewed by Cursor Bugbot for commit 57d23c5. 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: 351fc556-450b-415f-8e3b-b7f4632cd7fd

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
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/server/src/provider/Layers/CodexSessionRuntime.ts
Comment thread apps/web/src/webThreadOutbox.ts
@macroscopeapp

macroscopeapp Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a new 'Messages While Working' feature with steer/queue behavior, implementing a complete message queueing system (web) and turn-steering capability (server). The scope includes new user settings, UI changes, state management, and cross-cutting runtime behavior changes that warrant careful review.

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

@chrisdeeming

Copy link
Copy Markdown
Contributor

@ClapFy this is a great start are you in a position to continue working on this until the AI reviews are clean?

Some additional feedback:

  • 🟠 starting is treated as steerable, but codex only steers once running. concurrent sends can issue a second turn/start. mobile server
  • 🟠 a turn completing between the runtime snapshot and turn/steer loses the already-persisted prompt as a session error. code
  • 🟠 simultaneous cross-tab queue writes can overwrite each other and silently drop a prompt. the merge and full-key write are not atomic. code
  • 🟠 web defaults to steer before settings hydration. a user configured for queue can accidentally steer immediately after reload. settings send
  • 🟠 mobile and web snapshot drafts after editable awaits. content typed after tapping send can be consumed or cleared. mobile web
  • 🟠 web only drains the currently mounted thread. navigating away leaves queued messages unsent until that thread is revisited.
  • 🟠 failed-message pause state is memory-only. reload or another tab automatically retries despite the explicit retry contract. code

@ClapFy
ClapFy force-pushed the agent/active-turn-message-controls branch from 56e5d17 to 508098f Compare August 8, 2026 12:14
@github-actions github-actions Bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Aug 8, 2026

@macroscopeapp macroscopeapp 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.

Effect service conventions review: one finding in apps/server/src/provider/Layers/CodexSessionRuntime.ts (tagged-failure recovery should use Effect.catchTags). Everything else in scope (new contracts schema, mobile preference atoms, web outbox store) looked consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/provider/Layers/CodexSessionRuntime.ts Outdated
Comment thread apps/web/src/webThreadOutbox.ts
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/WebThreadOutboxDrain.tsx
Comment thread apps/server/src/provider/Layers/CodexSessionRuntime.ts Outdated
Comment thread apps/web/src/components/WebThreadOutboxDrain.tsx
Comment thread apps/web/src/webThreadOutbox.ts
Comment thread apps/server/src/provider/Layers/CodexSessionRuntime.ts

@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 1 potential issue.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 57d23c5. Configure here.

...(input.effort ? { effort: input.effort } : {}),
...(input.interactionMode ? { interactionMode: input.interactionMode } : {}),
});
const rawResponse = yield* client.raw.request("turn/start", params);

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.

Steer fallback starts second turn

High Severity

When turn/steer fails with CodexAppServerRequestError, sendTurn falls through to turn/start without re-reading whether the session is still running. A rejected steer while the active turn is alive can therefore open a second overlapping Codex turn.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 57d23c5. Configure here.

@ClapFy

ClapFy commented Aug 8, 2026

Copy link
Copy Markdown
Author

@chrisdeeming i fixed the issues but the ai checks are stick on the "Pending" status.

@chrisdeeming

Copy link
Copy Markdown
Contributor

@ClapFy it looks like the functionality from this PR is actually covered by a future rewrite of the T3 Code orchestrator.

See #2829 for that work and #4245 which implemented the same feature as this PR but closed in favour of waiting for the orchestrator v2 to land.

@ClapFy

ClapFy commented Aug 9, 2026

Copy link
Copy Markdown
Author

@ClapFy it looks like the functionality from this PR is actually covered by a future rewrite of the T3 Code orchestrator.

See #2829 for that work and #4245 which implemented the same feature as this PR but closed in favour of waiting for the orchestrator v2 to land.

The PR also contains a fix for a chat bug with Codex where you sometimes just can't stop the agent

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

Labels

size:XXL 1,000+ 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.

2 participants