diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-09 03:10:44 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-09 04:26:19 +0300 |
| commit | 120001b591e64d80a557d81e3b112460b9987fc6 (patch) | |
| tree | bcdf3e71ca2818d76e53f94d1be685e31d4ce6be /src | |
| parent | 55f5a3b5d4ab90b7df207531d3cd3cd5f0923616 (diff) | |
fix: keep non-claude pty working transitions screenless
Diffstat (limited to 'src')
| -rw-r--r-- | src/pane.rs | 97 | ||||
| -rw-r--r-- | src/pane/agent_detection.rs | 126 |
2 files changed, 215 insertions, 8 deletions
diff --git a/src/pane.rs b/src/pane.rs index f1d1c699..9fcddce0 100644 --- a/src/pane.rs +++ b/src/pane.rs @@ -31,10 +31,11 @@ mod xtgettcap; use self::agent_detection::{ agent_caused_pty_activity_active, baseline_pty_causality, decide_detection_screen_read, - decide_screen_detection_publish, detection_update_for_publish, handle_skipped_detection_update, - observe_pty_output_activity, DetectionPublishDecision, DetectionScreenReadDecision, - DetectionScreenReadInput, PendingIdleConfirmation, PendingWorkingConfirmation, - PostTaintWorkingLease, PtyCausalityTracker, ScreenDetectionPublishInput, + decide_pty_working_publish_without_screen, decide_screen_detection_publish, + detection_update_for_publish, handle_skipped_detection_update, observe_pty_output_activity, + DetectionPublishDecision, DetectionScreenReadDecision, DetectionScreenReadInput, + PendingIdleConfirmation, PendingWorkingConfirmation, PostTaintWorkingLease, + PtyCausalityTracker, PtyWorkingPublishInput, ScreenDetectionPublishInput, AGENT_PENDING_IDLE_RECHECK, AGENT_STARTUP_GRACE_WINDOW, }; use self::terminal::{GhosttyPaneTerminal, PaneTerminal}; @@ -619,6 +620,50 @@ fn spawn_basic_detection_task( }) { DetectionScreenReadDecision::Read => {} DetectionScreenReadDecision::Skip => continue, + DetectionScreenReadDecision::EvaluatePtyWorking => { + match decide_pty_working_publish_without_screen( + PtyWorkingPublishInput { + agent, + current_state: state, + last_visible_blocker, + last_visible_working, + last_visible_signal_refresh, + pty_activity, + now, + }, + &mut pending_idle, + &mut pending_working, + &mut post_taint_working, + ) { + DetectionPublishDecision::NoPublish => {} + DetectionPublishDecision::Publish { + state: new_state, + visible_blocker, + visible_working, + process_exited: publish_process_exited, + } => { + apply_agent_detection_publish_update( + state_events.clone(), + pane_id, + agent, + AgentDetectionPublishUpdate { + state: new_state, + visible_blocker, + visible_working, + process_exited: publish_process_exited, + }, + now, + &mut state, + &mut last_visible_blocker, + &mut last_visible_working, + &mut last_visible_signal_refresh, + &mut foreground_shell_exit_reported, + ) + .await; + } + } + continue; + } DetectionScreenReadDecision::Publish { state: new_state, visible_blocker, @@ -2058,6 +2103,50 @@ impl PaneRuntime { }) { DetectionScreenReadDecision::Read => {} DetectionScreenReadDecision::Skip => continue, + DetectionScreenReadDecision::EvaluatePtyWorking => { + match decide_pty_working_publish_without_screen( + PtyWorkingPublishInput { + agent, + current_state: state, + last_visible_blocker, + last_visible_working, + last_visible_signal_refresh, + pty_activity, + now, + }, + &mut pending_idle, + &mut pending_working, + &mut post_taint_working, + ) { + DetectionPublishDecision::NoPublish => {} + DetectionPublishDecision::Publish { + state: new_state, + visible_blocker, + visible_working, + process_exited: publish_process_exited, + } => { + apply_agent_detection_publish_update( + state_events.clone(), + pane_id, + agent, + AgentDetectionPublishUpdate { + state: new_state, + visible_blocker, + visible_working, + process_exited: publish_process_exited, + }, + now, + &mut state, + &mut last_visible_blocker, + &mut last_visible_working, + &mut last_visible_signal_refresh, + &mut foreground_shell_exit_reported, + ) + .await; + } + } + continue; + } DetectionScreenReadDecision::Publish { state: new_state, visible_blocker, diff --git a/src/pane/agent_detection.rs b/src/pane/agent_detection.rs index 1a3e795a..53e2ea31 100644 --- a/src/pane/agent_detection.rs +++ b/src/pane/agent_detection.rs @@ -297,6 +297,7 @@ pub(super) fn should_skip_idle_screen_scan(input: IdleScreenScanSkipInput) -> bo pub(super) enum DetectionScreenReadDecision { Read, Skip, + EvaluatePtyWorking, Publish { state: AgentState, visible_blocker: bool, @@ -318,13 +319,16 @@ pub(super) struct DetectionScreenReadInput { pub(super) last_screen_scan_pty_output_seq: Option<u64>, } +fn agent_activity_veto_requires_screen(agent: Option<Agent>) -> bool { + matches!(agent, Some(Agent::Claude)) +} + pub(super) fn decide_detection_screen_read( input: DetectionScreenReadInput, ) -> DetectionScreenReadDecision { if !input.agent_changed && !input.process_exited && !input.pending_idle_active - && !input.pending_working_active && !input.post_taint_working_active && input.agent.is_some() && input @@ -339,7 +343,15 @@ pub(super) fn decide_detection_screen_read( visible_working: false, process_exited: false, }, - AgentState::Idle | AgentState::Unknown => DetectionScreenReadDecision::Read, + AgentState::Idle | AgentState::Unknown + if agent_activity_veto_requires_screen(input.agent) + && !input.pending_working_active => + { + DetectionScreenReadDecision::Read + } + AgentState::Idle | AgentState::Unknown => { + DetectionScreenReadDecision::EvaluatePtyWorking + } }; } @@ -527,6 +539,74 @@ pub(super) struct ScreenDetectionPublishInput<'a> { pub(super) now: std::time::Instant, } +#[derive(Debug, Clone, Copy)] +pub(super) struct PtyWorkingPublishInput { + pub(super) agent: Option<Agent>, + pub(super) current_state: AgentState, + pub(super) last_visible_blocker: bool, + pub(super) last_visible_working: bool, + pub(super) last_visible_signal_refresh: Option<std::time::Instant>, + pub(super) pty_activity: Option<PtyActivitySignal>, + pub(super) now: std::time::Instant, +} + +pub(super) fn decide_pty_working_publish_without_screen( + input: PtyWorkingPublishInput, + pending_idle: &mut PendingIdleConfirmation, + pending_working: &mut PendingWorkingConfirmation, + post_taint_working: &mut PostTaintWorkingLease, +) -> DetectionPublishDecision { + let previous_publish = DetectionPublishState { + state: input.current_state, + visible_blocker: input.last_visible_blocker, + visible_working: input.last_visible_working, + }; + let next_publish = DetectionPublishState { + state: AgentState::Working, + visible_blocker: false, + visible_working: false, + }; + let stable_refresh_due = stable_visible_signal_refresh_due( + previous_publish, + next_publish, + input.last_visible_signal_refresh, + input.now, + ); + + match decide_detection_transition( + DetectionTransitionInput { + agent: input.agent, + previous_publish, + next_publish, + agent_changed: false, + process_exited: false, + pty_activity: input.pty_activity, + stable_refresh_due, + content: "", + now: input.now, + }, + pending_idle, + pending_working, + post_taint_working, + ) { + DetectionTransitionDecision::NoPublish => DetectionPublishDecision::NoPublish, + DetectionTransitionDecision::PublishHeldWorkingBeforeExit => { + DetectionPublishDecision::Publish { + state: AgentState::Working, + visible_blocker: false, + visible_working: false, + process_exited: false, + } + } + DetectionTransitionDecision::PublishNext => DetectionPublishDecision::Publish { + state: AgentState::Working, + visible_blocker: false, + visible_working: false, + process_exited: false, + }, + } +} + pub(super) fn decide_screen_detection_publish( input: ScreenDetectionPublishInput<'_>, pending_idle: &mut PendingIdleConfirmation, @@ -844,9 +924,17 @@ mod tests { state: AgentState, pty_activity: PtyActivitySignal, ) -> DetectionScreenReadInput { + screen_read_input_for_agent(Some(Agent::Codex), state, pty_activity) + } + + fn screen_read_input_for_agent( + agent: Option<Agent>, + state: AgentState, + pty_activity: PtyActivitySignal, + ) -> DetectionScreenReadInput { DetectionScreenReadInput { state, - agent: Some(Agent::Codex), + agent, pending_idle_active: false, pending_working_active: false, post_taint_working_active: false, @@ -879,7 +967,7 @@ mod tests { AgentState::Idle, pty_activity(true, true, 10), )), - DetectionScreenReadDecision::Read + DetectionScreenReadDecision::EvaluatePtyWorking ); assert_eq!( decide_detection_screen_read(screen_read_input( @@ -918,6 +1006,15 @@ mod tests { AgentState::Idle, pty_activity(true, true, 11), )), + DetectionScreenReadDecision::EvaluatePtyWorking + ); + + assert_eq!( + decide_detection_screen_read(screen_read_input_for_agent( + Some(Agent::Claude), + AgentState::Idle, + pty_activity(true, true, 11), + )), DetectionScreenReadDecision::Read ); } @@ -947,6 +1044,27 @@ mod tests { } #[test] + fn screen_read_decision_keeps_active_pty_pending_working_screenless() { + let mut input = screen_read_input(AgentState::Idle, pty_activity(true, true, 11)); + input.pending_working_active = true; + assert_eq!( + decide_detection_screen_read(input), + DetectionScreenReadDecision::EvaluatePtyWorking + ); + + let mut input = screen_read_input_for_agent( + Some(Agent::Claude), + AgentState::Idle, + pty_activity(true, true, 11), + ); + input.pending_working_active = true; + assert_eq!( + decide_detection_screen_read(input), + DetectionScreenReadDecision::EvaluatePtyWorking + ); + } + + #[test] fn codex_transcript_viewer_suppresses_prompt_idle_publish() { let content = "/ T R A N S C R I P T /\n\n› yeah go ahead\n────────────────────────────────────────────────────────────────────────────────── 100% ─\n ↑/↓ to scroll pgup/pgdn to page home/end to jump\n q to quit esc to edit prev"; |
