diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-09 16:53:00 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-04-09 17:28:02 +0300 |
| commit | 949597957323d9883868cc12d2ce72c2117e835e (patch) | |
| tree | 0efbd81cc3a55aab777adf9a2c1a4e8342a2641a /src | |
| parent | 1cbd3129363d2ad867d2753ef176a944554591f6 (diff) | |
fix: harden codex detection for narrow panes
Diffstat (limited to 'src')
| -rw-r--r-- | src/detect.rs | 34 |
1 files changed, 28 insertions, 6 deletions
diff --git a/src/detect.rs b/src/detect.rs index b7ffa441..11b0f365 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -1,6 +1,6 @@ -//! Agent state detection via screen content pattern matching. +//! Agent state detection via terminal tail pattern matching. //! -//! Each pane's visible terminal content is read periodically and matched +//! Each pane's live bottom-of-buffer text is read periodically and matched //! against known agent output patterns to determine state. /// The detected state of a terminal pane. @@ -72,7 +72,7 @@ pub fn identify_agent_in_job(job: &crate::platform::ForegroundJob) -> Option<(Ag best.map(|(_, agent, name)| (agent, name)) } -/// Detect the state of an agent from the visible screen content. +/// Detect the state of an agent from the live terminal tail snapshot. /// If `agent` is `None`, returns `Unknown`. pub fn detect_state(agent: Option<Agent>, screen_content: &str) -> AgentState { let Some(agent) = agent else { @@ -166,7 +166,7 @@ fn detect_codex(content: &str) -> AgentState { // Blocked patterns if lower.contains("press enter to confirm or esc to cancel") - || lower.contains("| enter to submit answer") + || lower.contains("enter to submit answer") || lower.contains("allow command?") || lower.contains("[y/n]") || lower.contains("yes (y)") @@ -178,7 +178,7 @@ fn detect_codex(content: &str) -> AgentState { } // Working - if has_interrupt_pattern(&lower) { + if has_interrupt_pattern(&lower) || has_codex_working_header(content) { return AgentState::Working; } @@ -453,6 +453,13 @@ fn has_spinner_activity(content: &str) -> bool { false } +fn has_codex_working_header(content: &str) -> bool { + content.lines().any(|line| { + let trimmed = line.trim_start(); + trimmed.starts_with('•') && trimmed.contains("Working (") + }) +} + /// Cursor spinner: ⬡ or ⬢ followed by a word ending in "ing" fn has_cursor_spinner(content: &str) -> bool { for line in content.lines() { @@ -721,6 +728,16 @@ mod tests { } #[test] + fn codex_waiting_submit_answer_wrapped_footer() { + assert_eq!( + detect_codex( + "Question 1/2\nChoose an option.\nenter to submit answer\nesc to interrupt" + ), + AgentState::Blocked + ); + } + + #[test] fn codex_working_interrupt() { assert_eq!( detect_codex("generating code\nesc to interrupt"), @@ -729,6 +746,11 @@ mod tests { } #[test] + fn codex_working_truncated_status_header() { + assert_eq!(detect_codex("• Working (0s • esc…"), AgentState::Working); + } + + #[test] fn codex_idle() { assert_eq!(detect_codex("❯ "), AgentState::Idle); } @@ -1178,7 +1200,7 @@ mod tests { } #[test] - fn visible_screen_content_works_with_detection() { + fn terminal_tail_content_works_with_detection() { assert_eq!(detect_pi("Working..."), AgentState::Working); } |
