What version of the Codex App are you using?
Codex Desktop 26.513.20950 (2816)
What subscription do you have?
GPT Pro
What platform is your computer?
Darwin 24.5.0 arm64 arm
What issue are you seeing?
When editing a queued message in the Codex Desktop app on macOS, pressing Enter after the edit re-adds the message at the end of the queue instead of returning it to the position it had before editing.
That makes queue editing destructive to the intended execution order. If I have queued:
first queued
second queued
third queued
and I edit second queued, the edited message should still be item 2. Today it can come back after third queued.
Related but distinct: #16681 is about editing/removing pending steer messages. This report is about preserving the ordering of ordinary queued messages after editing.
What steps can reproduce the bug?
- Open Codex Desktop on macOS.
- Start a turn that keeps Codex working.
- Add several follow-up messages to the queue.
- Edit a queued message that is not the last queue item.
- Press
Enter to save/requeue the edited message.
- Observe that the edited message is placed at the end of the queue.
What is the expected behavior?
The edited queued message should replace its original queue slot. Editing should change the message content, not reorder the queue.
Expected result:
first queued
second edited
third queued
Actual result:
first queued
third queued
second edited
Additional information
I could not find the Codex Desktop renderer implementation in this public repo. The public TUI code has a related queue state path: editing a queued message pops it from queued_user_messages, while requeueing normal submitted input appends to the back. A small state marker for "the queue slot currently being edited" would make the subsequent submit replace the original slot instead of always appending.
The sketch below is based on the public TUI implementation. It may need to be mapped to the equivalent Desktop queue model if the Desktop renderer owns separate queue state.
diff --git a/codex-rs/tui/src/chatwidget/user_messages.rs b/codex-rs/tui/src/chatwidget/user_messages.rs
@@
pub(super) struct QueuedUserMessage {
pub(super) user_message: UserMessage,
pub(super) action: QueuedInputAction,
}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) struct QueuedUserMessageEdit {
+ pub(super) index: usize,
+}
@@
pub(crate) struct ThreadInputState {
pub(super) queued_user_messages: VecDeque<QueuedUserMessage>,
pub(super) queued_user_message_history_records: VecDeque<UserMessageHistoryRecord>,
+ pub(super) active_queued_user_message_edit: Option<QueuedUserMessageEdit>,
pub(super) user_turn_pending_start: bool,
}
diff --git a/codex-rs/tui/src/chatwidget/input_queue.rs b/codex-rs/tui/src/chatwidget/input_queue.rs
@@
use super::QueuedUserMessage;
+use super::QueuedUserMessageEdit;
use super::UserMessage;
use super::UserMessageHistoryRecord;
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) enum QueuedUserMessageInsert {
+ Front,
+ Back,
+}
+
pub(super) struct InputQueueState {
pub(super) queued_user_messages: VecDeque<QueuedUserMessage>,
+ pub(super) active_queued_user_message_edit: Option<QueuedUserMessageEdit>,
pub(super) queued_user_message_history_records: VecDeque<UserMessageHistoryRecord>,
}
@@
pub(super) fn clear(&mut self) {
self.queued_user_messages.clear();
+ self.active_queued_user_message_edit = None;
self.queued_user_message_history_records.clear();
}
+
+ pub(super) fn insert_queued_user_message(
+ &mut self,
+ user_message: UserMessage,
+ action: crate::bottom_pane::QueuedInputAction,
+ history_record: UserMessageHistoryRecord,
+ fallback: QueuedUserMessageInsert,
+ ) {
+ let queued_message = QueuedUserMessage::new(user_message, action);
+ if let Some(edit) = self.active_queued_user_message_edit.take() {
+ let index = edit.index.min(self.queued_user_messages.len());
+ self.queued_user_messages.insert(index, queued_message);
+ self.queued_user_message_history_records.insert(index, history_record);
+ } else if fallback == QueuedUserMessageInsert::Back {
+ self.queued_user_messages.push_back(queued_message);
+ self.queued_user_message_history_records.push_back(history_record);
+ } else {
+ self.queued_user_messages.push_front(queued_message);
+ self.queued_user_message_history_records.push_front(history_record);
+ }
+ }
}
diff --git a/codex-rs/tui/src/chatwidget/input_restore.rs b/codex-rs/tui/src/chatwidget/input_restore.rs
@@
pub(super) fn pop_latest_queued_user_message(&mut self) -> Option<UserMessage> {
if let Some(user_message) = self.input_queue.queued_user_messages.pop_back() {
+ self.input_queue.active_queued_user_message_edit = Some(QueuedUserMessageEdit {
+ index: self.input_queue.queued_user_messages.len(),
+ });
let history_record = self
.input_queue
.queued_user_message_history_records
@@
} else {
+ self.input_queue.active_queued_user_message_edit = None;
let user_message = self.input_queue.rejected_steers_queue.pop_back()?;
}
}
@@
queued_user_messages: self.input_queue.queued_user_messages.clone(),
queued_user_message_history_records: self.input_queue.queued_user_message_history_records.clone(),
+ active_queued_user_message_edit: self.input_queue.active_queued_user_message_edit,
@@
self.input_queue.queued_user_message_history_records.resize(
self.input_queue.queued_user_messages.len(),
UserMessageHistoryRecord::UserMessageText,
);
+ self.input_queue.active_queued_user_message_edit =
+ input_state.active_queued_user_message_edit;
diff --git a/codex-rs/tui/src/chatwidget/input_flow.rs b/codex-rs/tui/src/chatwidget/input_flow.rs
@@
if !self.is_session_configured() || self.is_user_turn_pending_or_running() {
- self.input_queue.queued_user_messages.push_back(QueuedUserMessage::new(user_message, action));
- self.input_queue.queued_user_message_history_records.push_back(UserMessageHistoryRecord::UserMessageText);
+ self.input_queue.insert_queued_user_message(
+ user_message,
+ action,
+ UserMessageHistoryRecord::UserMessageText,
+ QueuedUserMessageInsert::Back,
+ );
self.refresh_pending_input_preview();
} else {
+ self.input_queue.active_queued_user_message_edit = None;
self.submit_user_message(user_message);
}
}
diff --git a/codex-rs/tui/src/chatwidget/input_submission.rs b/codex-rs/tui/src/chatwidget/input_submission.rs
@@
if !self.is_session_configured() {
- self.input_queue.queued_user_messages.push_front(QueuedUserMessage::from(user_message));
- self.input_queue.queued_user_message_history_records.push_front(history_record);
+ self.input_queue.insert_queued_user_message(
+ user_message,
+ QueuedInputAction::Plain,
+ history_record,
+ QueuedUserMessageInsert::Front,
+ );
self.refresh_pending_input_preview();
return (true, None);
}
@@
if user_message.text.is_empty()
&& user_message.local_images.is_empty()
&& user_message.remote_image_urls.is_empty()
{
+ self.input_queue.active_queued_user_message_edit = None;
return (false, None);
}
diff --git a/codex-rs/tui/src/chatwidget/tests/composer_submission.rs b/codex-rs/tui/src/chatwidget/tests/composer_submission.rs
@@
+#[tokio::test]
+async fn edited_queued_message_requeues_at_original_position() {
+ let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
+ chat.bottom_pane.set_task_running(true);
+
+ for text in ["first queued", "second queued", "third queued"] {
+ chat.input_queue.queued_user_messages.push_back(UserMessage::from(text).into());
+ chat.input_queue
+ .queued_user_message_history_records
+ .push_back(UserMessageHistoryRecord::UserMessageText);
+ }
+
+ let queued_message = chat.input_queue.queued_user_messages.remove(1).unwrap();
+ let history_record = chat.input_queue.queued_user_message_history_records.remove(1).unwrap();
+ chat.input_queue.active_queued_user_message_edit = Some(QueuedUserMessageEdit { index: 1 });
+ chat.restore_user_message_to_composer(user_message_for_restore(
+ queued_message.into_user_message(),
+ &history_record,
+ ));
+
+ chat.bottom_pane.set_composer_text("second edited".to_string(), Vec::new(), Vec::new());
+ chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
+
+ assert_eq!(
+ chat.queued_user_message_texts(),
+ vec!["first queued", "second edited", "third queued"]
+ );
+ assert!(chat.input_queue.active_queued_user_message_edit.is_none());
+}
Local checks attempted:
git diff --check passed.
cargo fmt ran, with warnings about imports_granularity = Item requiring nightly.
cargo test -p codex-tui edited_queued_message_requeues_at_original_position was blocked before compilation because the local Homebrew Rust toolchain is rustc 1.89.0, while current dependencies require Rust 1.91.x.
What version of the Codex App are you using?
Codex Desktop
26.513.20950 (2816)What subscription do you have?
GPT Pro
What platform is your computer?
Darwin 24.5.0 arm64 armWhat issue are you seeing?
When editing a queued message in the Codex Desktop app on macOS, pressing
Enterafter the edit re-adds the message at the end of the queue instead of returning it to the position it had before editing.That makes queue editing destructive to the intended execution order. If I have queued:
first queuedsecond queuedthird queuedand I edit
second queued, the edited message should still be item 2. Today it can come back afterthird queued.Related but distinct: #16681 is about editing/removing pending steer messages. This report is about preserving the ordering of ordinary queued messages after editing.
What steps can reproduce the bug?
Enterto save/requeue the edited message.What is the expected behavior?
The edited queued message should replace its original queue slot. Editing should change the message content, not reorder the queue.
Expected result:
first queuedsecond editedthird queuedActual result:
first queuedthird queuedsecond editedAdditional information
I could not find the Codex Desktop renderer implementation in this public repo. The public TUI code has a related queue state path: editing a queued message pops it from
queued_user_messages, while requeueing normal submitted input appends to the back. A small state marker for "the queue slot currently being edited" would make the subsequent submit replace the original slot instead of always appending.The sketch below is based on the public TUI implementation. It may need to be mapped to the equivalent Desktop queue model if the Desktop renderer owns separate queue state.
Local checks attempted:
git diff --checkpassed.cargo fmtran, with warnings aboutimports_granularity = Itemrequiring nightly.cargo test -p codex-tui edited_queued_message_requeues_at_original_positionwas blocked before compilation because the local Homebrew Rust toolchain isrustc 1.89.0, while current dependencies require Rust1.91.x.