feat(tui): steer messages into the ongoing stream, add a /settings send-mode switch#3553
Merged
Conversation
While the agent is working, a plain send now attaches the message to the ongoing stream through the runtime steer queue, matching the lean TUI. The agent picks it up mid-turn without breaking the stream, and the transcript shows the message at the point the agent actually saw it. A new ctrl+q binding (action editor_queue, remappable) submits the message to the local queue instead, preserving the previous end-of-turn behavior. Fork-mode skill commands always queue since they spawn their own stream. A rejected steer (full queue, stream just stopped) falls back to the queue path so the message is never dropped. Issue #3547
Drive the real TUI through the tuitest harness against a replaying VCR proxy in simulated-stream mode (250ms per SSE chunk, one character per chunk) so the tests get a deterministic mid-stream window. The steering scenario submits a follow-up while the first answer streams and asserts the steering toast, the injected user bubble, and the follow-up answer within the same stream. The ctrl+q scenario asserts the queue toast and end-of-turn dispatch. The follow-up draft is pasted as a single tea.PasteMsg because per-key sendSync roundtrips are slow enough under -race to consume the whole streaming window.
… send mode - rename the layout dialog command to /settings with Visuals and Behavior tabs - the Behavior tab switches what busy sends do: steer (default) or queue, persisted as settings.busy_send_mode in the user config - drop the ctrl+q editor_queue shortcut in favor of the saved switch - open /settings with only the Behavior tab when the sidebar is hidden
dgageot
approved these changes
Jul 9, 2026
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.
Closes #3547.
While the agent is working, a plain send in the full TUI now attaches the message to the ongoing stream via the runtime steer queue (the behavior the lean TUI already has). The previous end-of-turn behavior remains available as a persisted setting: the
/customcommand becomes/settings, a tabbed dialog whose Behavior tab switches busy sends betweenSteer(default) andQueue.Issue expectations
Runtime.Steer; the loop drains it mid-turn and emitsUserMessageEvent, so the transcript shows the message where the agent saw itctrl + qto add messages to queue"/settingsholds a Steer/Queue radio switch, persisted assettings.busy_send_modein the user config and applied to every tabThe /settings dialog
/custom(layout dialog) is renamed to/settingsand gains tabs:/customcontent: live schematic preview, sidebar position, section spacing, section visibilityWhile agent is working:Steer(attach mid-turn, default) orQueue(hold until the turn ends)Actual render of the Behavior tab:
Settings updatedtoast); Esc cancels and rolls back the layout preview. The send mode never previews, so it needs no rollback.--sidebar=falsethe dialog opens with only the Behavior tab (previously/customwas rejected entirely). Lean mode still drops the open message: it has no overlay support, an open dialog would invisibly capture keystrokes.busy_send_modeis cleared when set back to steer, keeping the config file minimal.Flow
Notes
UserMessageEvent, so transcript order matches what the model actually saw.ctrl+qbinding and no neweditor_queueaction: the key stays free for user remaps and the docs keep their previous examples.SendMsg.Queueremains as an internal flag for the steer-rejection fallback.App.Run's attachment processing was extracted tobuildUserMultiContentand reused byApp.SteerMessage; no behavior change./customshipped in v1.102.0; no alias is kept for it. If a transition alias seems safer, it can be added.Testing
TestChat_SteerWhileStreamingandTestChat_QueueSendModeWhileStreamingdrive the real TUI through tuitest against a VCR proxy in simulated-stream mode (250ms per SSE chunk); the queue test flips the switch through the actual/settingsdialog and assertsbusy_send_mode: queuelands in the config filepkg/tui/...,pkg/app,pkg/runtime,pkg/userconfig, fulle2e/tui; golangci-lint reports 0 issues