Problem
When the user presses Enter on a non-empty composer while the agent is already working, the message goes to one of four different fates with no UI indication of which one fired. Behavior depends on fast-changing internal state the user has no visibility into.
From crates/tui/src/tui/app.rs:759-772:
pub enum SubmitDisposition {
/// Engine idle and online: send immediately.
Immediate,
/// Offline mode: park on `queued_messages`.
Queue,
/// Engine busy and online: forward as a mid-turn steer.
Steer,
/// Model is actively streaming text; park on `queued_messages` for
/// dispatch after TurnComplete.
QueueFollowUp,
}
So pressing Enter mid-turn does one of:
- Steer — interrupts the model with a redirect (engine busy, not streaming).
- QueueFollowUp — silently parks for after the turn completes (engine busy, currently streaming text).
- Queue — parks because we're offline.
- (Or sends immediately if the timing was right.)
The boundary between Steer and QueueFollowUp is "is the model emitting text right now?" — which flips back and forth multiple times within a single turn (text → tool call → text → tool call → final text). The user pressing Enter at the wrong millisecond can't tell whether they're steering or queuing.
What's missing
- No visual signal of the active disposition while typing. The send-button or footer should change icon/label depending on what Enter will do right now.
- No explicit "steer" vs "queue" key. Today both go through Enter and the disposition is implicit. Power users want a deterministic chord (e.g.
Ctrl+Enter = always steer, Shift+Enter = always queue, Enter = use disposition).
- Queued messages are visually quiet. The composer probably hints at queue depth somewhere but the user reports inconsistent feedback. Verify: is there a queued-count indicator, and does it survive the streaming → idle transition reliably?
- Steer feedback is unclear. When a steer succeeds, the user should see something in the transcript or footer confirming "your message was injected mid-turn at sequence N." Right now this may or may not surface.
Fix
Two layers:
1. Make the disposition visible before Enter
In the composer chrome (or send-button hint, or footer), show what Enter will do right now:
→ send — Immediate
↳ steer — Steer (engine running tools)
⏱ queue (after turn) — QueueFollowUp (model streaming text)
📭 queue (offline) — Queue (offline)
Update on every state change (TurnStarted, StreamingStarted, StreamingFinished, TurnComplete, OfflineEnter/Exit).
2. Give power users explicit chords
Enter — current behavior (auto-disposition).
Ctrl+Enter — force Steer (warn if not applicable).
Shift+Enter — force Queue (always parks for after turn).
Document in /help and the keybindings.json.
3. Confirm queue/steer outcomes
- Steer success → toast or transcript marker "message steered into turn".
- Queue → footer chip showing depth (
📥 2 queued) that's always visible until drained.
Acceptance criteria
Touches: crates/tui/src/tui/app.rs::decide_submit_disposition, the composer view (crates/tui/src/tui/views/), footer renderer in tui/ui.rs, key dispatch.
Problem
When the user presses Enter on a non-empty composer while the agent is already working, the message goes to one of four different fates with no UI indication of which one fired. Behavior depends on fast-changing internal state the user has no visibility into.
From
crates/tui/src/tui/app.rs:759-772:So pressing Enter mid-turn does one of:
The boundary between Steer and QueueFollowUp is "is the model emitting text right now?" — which flips back and forth multiple times within a single turn (text → tool call → text → tool call → final text). The user pressing Enter at the wrong millisecond can't tell whether they're steering or queuing.
What's missing
Ctrl+Enter= always steer,Shift+Enter= always queue,Enter= use disposition).Fix
Two layers:
1. Make the disposition visible before Enter
In the composer chrome (or send-button hint, or footer), show what Enter will do right now:
→ send— Immediate↳ steer— Steer (engine running tools)⏱ queue (after turn)— QueueFollowUp (model streaming text)📭 queue (offline)— Queue (offline)Update on every state change (TurnStarted, StreamingStarted, StreamingFinished, TurnComplete, OfflineEnter/Exit).
2. Give power users explicit chords
Enter— current behavior (auto-disposition).Ctrl+Enter— force Steer (warn if not applicable).Shift+Enter— force Queue (always parks for after turn).Document in
/helpand the keybindings.json.3. Confirm queue/steer outcomes
📥 2 queued) that's always visible until drained.Acceptance criteria
Ctrl+EnterandShift+Enterchords land with explicit semantics./helpand keybindings docs updated.Touches:
crates/tui/src/tui/app.rs::decide_submit_disposition, the composer view (crates/tui/src/tui/views/), footer renderer intui/ui.rs, key dispatch.