mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Queue follow-up input during user shell commands (#18820)
Fixes #17954. ## Why When a manual shell command like `!sleep 10` is running, submitting plain text such as `hi` currently sends that text as a steer for the active shell turn. User shell turns are not steerable like model turns, so the TUI can remain stuck in `Working` after the shell command finishes. ## What Changed - Detect when the only active work is one or more `ExecCommandSource::UserShell` commands. - Queue plain submitted input in that state so it drains after the shell command and shell turn complete. - Preserve `!cmd` submissions during running work so explicit shell commands keep their existing behavior. - Add regression coverage for the `!sleep 10` plus `hi` flow in `chatwidget::tests::exec_flow::user_message_during_user_shell_command_is_queued_not_steered`. ## Verification - Manually confirmed hang before the fix and no hang after the fix
This commit is contained in:
@@ -1036,6 +1036,58 @@ async fn bang_shell_enter_while_task_running_submits_run_user_shell_command() {
|
||||
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_message_during_user_shell_command_is_queued_not_steered() {
|
||||
let (mut chat, _rx, mut op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
chat.handle_codex_event(Event {
|
||||
id: "turn-start".into(),
|
||||
msg: EventMsg::TurnStarted(TurnStartedEvent {
|
||||
turn_id: "turn-1".to_string(),
|
||||
started_at: None,
|
||||
model_context_window: None,
|
||||
collaboration_mode_kind: ModeKind::Default,
|
||||
}),
|
||||
});
|
||||
let begin = begin_exec_with_source(
|
||||
&mut chat,
|
||||
"user-shell-sleep",
|
||||
"sleep 10",
|
||||
ExecCommandSource::UserShell,
|
||||
);
|
||||
|
||||
assert!(chat.only_user_shell_commands_running());
|
||||
chat.bottom_pane
|
||||
.set_composer_text("hi".to_string(), Vec::new(), Vec::new());
|
||||
chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
|
||||
assert_matches!(op_rx.try_recv(), Err(TryRecvError::Empty));
|
||||
assert_eq!(chat.queued_user_message_texts(), vec!["hi".to_string()]);
|
||||
|
||||
end_exec(&mut chat, begin, "", "", /*exit_code*/ 0);
|
||||
chat.handle_codex_event(Event {
|
||||
id: "turn-complete".into(),
|
||||
msg: EventMsg::TurnComplete(TurnCompleteEvent {
|
||||
turn_id: "turn-1".to_string(),
|
||||
last_agent_message: Some("done".to_string()),
|
||||
completed_at: None,
|
||||
duration_ms: None,
|
||||
}),
|
||||
});
|
||||
|
||||
match next_submit_op(&mut op_rx) {
|
||||
Op::UserTurn { items, .. } => assert_eq!(
|
||||
items,
|
||||
vec![UserInput::Text {
|
||||
text: "hi".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}]
|
||||
),
|
||||
other => panic!("expected queued user message after shell completion, got {other:?}"),
|
||||
}
|
||||
assert!(chat.queued_user_messages.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn disabled_slash_command_while_task_running_snapshot() {
|
||||
// Build a chat widget and simulate an active task
|
||||
|
||||
Reference in New Issue
Block a user