Files
codex/codex-rs/tui/src/app/agent_status_feed_tests.rs
T
rka-oai 2c79ee6dac Add structured asynchronous user input requests (#42178)
## What changed

- Replace `send_user_message_async` with `request_user_input_async`, accepting one or more questions with optional suggested answers while allowing the turn to continue.
- Attach structured question metadata to asynchronous agent messages while retaining readable fallback text, and preserve it through app-server events, thread history, and generated schemas.
- Continue enabling the new tool for model catalogs that advertise either the old or new tool name.

## Testing

- Cover tool registration, question validation, emitted items, continued turn execution, history preservation, and backwards-compatible deserialization.

GitOrigin-RevId: ffc49aebde2e854c3c50a122aa52805fec3fc6b3
2026-09-02 02:09:35 +00:00

116 lines
4.0 KiB
Rust

use super::*;
use codex_app_server_protocol::CommandExecutionSource;
use codex_app_server_protocol::CommandExecutionStatus;
use codex_app_server_protocol::ItemCompletedNotification;
use codex_utils_absolute_path::AbsolutePathBuf;
#[test]
fn agent_status_uses_bounded_buffered_activity() {
let mut store = ThreadEventStore::new(/*capacity*/ 8);
store.push_notification(ServerNotification::ItemCompleted(
ItemCompletedNotification {
item: ThreadItem::CommandExecution {
id: "command-1".to_string(),
command: "cargo test -p codex-tui".to_string(),
cwd: AbsolutePathBuf::try_from("/workspace")
.expect("absolute path")
.into(),
process_id: None,
plugin_id: None,
script_path: None,
source: CommandExecutionSource::Agent,
status: CommandExecutionStatus::Completed,
command_actions: Vec::new(),
aggregated_output: Some("unbounded output\n".repeat(10_000)),
exit_code: Some(0),
duration_ms: Some(42),
},
thread_id: "thread-child".to_string(),
turn_id: "turn-1".to_string(),
completed_at_ms: 1,
},
));
store.push_notification(ServerNotification::ItemCompleted(
ItemCompletedNotification {
item: ThreadItem::AgentMessage {
id: "message-1".to_string(),
text: "Finished checking the focused TUI tests.".to_string(),
phase: None,
memory_citation: None,
delivery: None,
questions: None,
},
thread_id: "thread-child".to_string(),
turn_id: "turn-1".to_string(),
completed_at_ms: 2,
},
));
let preview = AgentStatusThreadPreview::from_store("/root/reviewer".to_string(), &store);
let cell = AgentStatusHistoryCell::new(vec![preview]);
let rendered = cell
.display_lines(/*width*/ 80)
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join("\n");
insta::assert_snapshot!(rendered, @r###"
/subagents
Sub-agents running
• `/root/reviewer`
$ cargo test -p codex-tui
Finished checking the focused TUI tests.
"###);
assert!(!rendered.contains("unbounded output"));
}
#[test]
fn agent_status_uses_reasoning_summaries_only() {
let mut store = ThreadEventStore::new(/*capacity*/ 8);
store.push_notification(ServerNotification::ItemCompleted(
ItemCompletedNotification {
item: ThreadItem::Reasoning {
id: "reasoning-with-summary".to_string(),
summary: vec!["safe summary".to_string()],
content: vec!["hidden raw reasoning".to_string()],
},
thread_id: "thread-child".to_string(),
turn_id: "turn-1".to_string(),
completed_at_ms: 1,
},
));
store.push_notification(ServerNotification::ItemCompleted(
ItemCompletedNotification {
item: ThreadItem::Reasoning {
id: "reasoning-without-summary".to_string(),
summary: Vec::new(),
content: vec!["raw-only reasoning".to_string()],
},
thread_id: "thread-child".to_string(),
turn_id: "turn-1".to_string(),
completed_at_ms: 2,
},
));
let preview = AgentStatusThreadPreview::from_store("/root/reviewer".to_string(), &store);
let cell = AgentStatusHistoryCell::new(vec![preview]);
let rendered = cell
.display_lines(/*width*/ 80)
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join("\n");
insta::assert_snapshot!(rendered, @r###"
/subagents
Sub-agents running
• `/root/reviewer`
safe summary
"###);
assert!(!rendered.contains("hidden raw reasoning"));
assert!(!rendered.contains("raw-only reasoning"));
}