feat(tui): add edit and discard actions for queued messages #6271
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.
Summary
Add the ability to edit and discard queued messages while the agent is working.
This allows users to fix typos, rephrase, or completely remove messages from
the queue before they are processed.
New keybinds:
<leader>i- Edit the last queued message<leader>d- Discard the last queued messageMotivation
When iterating quickly with the AI assistant, users often queue multiple messages.
Currently, there's no way to modify or remove these queued messages once sent.
This leads to:
This feature addresses a common workflow need requested by multiple users.
Related Issues & PRs
Closes
Supersedes
Related
Screenshots
Queued message with hints
Editing a queued message
Command palette with queue commands
Demo
https://streamable.com/urjeww
The video demonstrates the complete flow:
<leader>i<leader>dImplementation
Why this PR exists (context on #5415)
This implementation started from PR #5415 which was stale and had several issues:
Problems with the original PR #5415
history_previouskeybind - Hijacked the UP arrow when prompt was empty, conflicting with normal history navigationWhat we fixed/improved
<leader>i/<leader>dArchitecture Decisions
Why dedicated keybinds instead of UP arrow?
The original PR #5415 proposed reusing
history_previous(UP arrow) when the promptis empty. We chose dedicated keybinds (
<leader>iand<leader>d) because:<leader>hfor tips,<leader>upfor parent session)Why separate Edit and Discard?
<leader>i): Loads the message into the prompt for modification<leader>d): Removes the message entirely without loading itThis separation allows:
Why only edit the last queued message?
Both OpenAI Codex and our implementation only allow editing the most recent queued message.
Rationale:
State Management
We track
editingQueuedMessageIDin the prompt store to:EDITINGbadge on the correct messageFile/Image Preservation
When loading a queued message for editing, we preserve non-text parts by reconstructing them with virtual text representations (e.g.,
[File: filename]or[Image 1]) and creating extmarks for proper rendering. This ensures images and files aren't silently lost when editing.Comparison with Other Tools
We studied how other AI coding assistants handle queued message editing:
OpenAI Codex (Rust TUI)
Source:
codex-rs/tui/src/chatwidget.rsCodex stores queued messages in a
VecDeque<UserMessage>with text and image paths. It usesAlt+Upto pop the most recent queued message back into the composer, preserving image paths separately. A static hint "Alt+Up edit" is shown on queued messages.What we learned from Codex:
How we differ from Codex:
Alt+Up<leader>i<leader>dEDITINGbadgeClaude Code
Claude Code allows editing queued messages via UP arrow when prompt is empty.
This is similar to the original #5415 approach, which we improved upon with
dedicated keybinds to avoid conflicts with history navigation.
Flow Diagram
API Endpoints Added
GET/session/:sessionID/queueGET/session/:sessionID/queue/:messageIDDELETE/session/:sessionID/queue/:messageIDChanges
Backend (
packages/opencode/src/)session/prompt.tsqueued(),getQueued(),cancelQueued()functions; trackmessageIDin callbacksserver/server.tsconfig/config.tsqueue_editandqueue_discardkeybind defaultsFrontend (
packages/opencode/src/cli/cmd/tui/)component/prompt/index.tsxeditingQueuedMessageIDstate; preserve file parts with extmarks; handle edge cases (message processed mid-edit, cancel with ctrl+c, clear with ctrl+u)component/dialog-command.tsxroutes/session/index.tsxSDK (auto-generated)
packages/sdk/js/src/v2/gen/*packages/sdk/openapi.jsonBug fix: disabled commands triggering via keybinds
Found and fixed a bug in
dialog-command.tsxwhere disabled commands could still be triggered via their keybinds:This affected all commands with a
disabledproperty, not just the queue commands.Edge Cases
[File: filename]with extmarksctrl+c)ctrl+u)How to Test
QUEUEDbadge)ctrl+x ito edit the queued messageEnterto submit, orctrl+cto cancelctrl+x dto discard without editingTesting
test/session/queue.test.ts(new file)SessionPrompt.queued()- returns empty array for non-existent sessionSessionPrompt.getQueued()- returns undefined for non-existent session/messageSessionPrompt.cancelQueued()- returns undefined for non-existent session/messagetest/config/config.test.ts(additions)queue_editdefaults to<leader>iqueue_discarddefaults to<leader>dBreaking Changes
None. This is an additive feature with new keybinds that don't conflict with existing ones.