diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-10 21:00:56 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-10 21:00:56 +0300 |
| commit | 0efd8eaad1620dc4b9e44c45ee16ef0a9807a200 (patch) | |
| tree | 66fe2ea815e0cb3f9832a95cc7bfd18cacc194ed /src/pane.rs | |
| parent | 9e0dd9412f7b2b8b2dbabf5ddcd523b4d868aed2 (diff) | |
fix: remove pty taint from agent detection
Diffstat (limited to 'src/pane.rs')
| -rw-r--r-- | src/pane.rs | 216 |
1 files changed, 42 insertions, 174 deletions
diff --git a/src/pane.rs b/src/pane.rs index b0f3bbfa..a5709ebb 100644 --- a/src/pane.rs +++ b/src/pane.rs @@ -32,12 +32,11 @@ mod terminal; 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_with_osc, - handle_skipped_detection_update, observe_pty_output_activity, DetectionPublishDecision, - DetectionScreenReadDecision, DetectionScreenReadInput, PendingIdleConfirmation, - PendingWorkingConfirmation, PostTaintWorkingLease, PtyCausalityTracker, - ScreenDetectionPublishInput, AGENT_PENDING_IDLE_RECHECK, AGENT_STARTUP_GRACE_WINDOW, + decide_detection_screen_read, decide_screen_detection_publish, + detection_update_for_publish_with_osc, mark_detection_content_changed, + observe_detection_content_change, DetectionPublishDecision, DetectionScreenReadDecision, + DetectionScreenReadInput, PendingIdleConfirmation, ScreenDetectionPublishInput, + AGENT_PENDING_IDLE_RECHECK, AGENT_STARTUP_GRACE_WINDOW, }; use self::terminal::{GhosttyPaneTerminal, PaneTerminal}; pub(crate) use self::terminal::{TerminalDirtyPatch, TerminalDirtyPatchOutcome}; @@ -384,8 +383,7 @@ fn spawn_basic_detection_task( pane_id: PaneId, child_pid: Arc<AtomicU32>, terminal: Arc<PaneTerminal>, - pty_output_seq: Arc<AtomicU64>, - input_write_seq: Arc<AtomicU64>, + detection_content_seq: Arc<AtomicU64>, full_lifecycle_authority_active: Arc<AtomicBool>, state_events: mpsc::Sender<AppEvent>, ) -> ( @@ -414,18 +412,12 @@ fn spawn_basic_detection_task( let mut foreground_shell_exit_reported = false; let mut release_was_active = false; let mut last_detection_text = String::new(); - let mut last_screen_scan_pty_output_seq = None; - let mut pty_causality = PtyCausalityTracker::default(); + let mut last_screen_scan_detection_content_seq = None; let mut agent_startup_grace_until = None; let mut pending_idle = PendingIdleConfirmation::default(); - let mut pending_working = PendingWorkingConfirmation::default(); - let mut post_taint_working = PostTaintWorkingLease::default(); loop { - let now_for_sleep = std::time::Instant::now(); - let sleep_duration = if pending_working.active() { - pending_working.recheck_delay(now_for_sleep) - } else if pending_idle.active() { + let sleep_duration = if pending_idle.active() { AGENT_PENDING_IDLE_RECHECK } else { std::time::Duration::from_millis(300) @@ -448,12 +440,9 @@ fn spawn_basic_detection_task( foreground_shell_exit_reported = false; release_was_active = false; last_detection_text.clear(); - last_screen_scan_pty_output_seq = None; - pty_causality = PtyCausalityTracker::default(); + last_screen_scan_detection_content_seq = None; agent_startup_grace_until = None; pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); } } @@ -541,19 +530,12 @@ fn spawn_basic_detection_task( agent_changed = previous_agent != agent; if agent_changed { pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); - last_screen_scan_pty_output_seq = None; + last_screen_scan_detection_content_seq = None; // A new foreground agent must not inherit OSC // title/progress evidence from the previous process. terminal.clear_agent_osc_state(); if agent.is_some() { agent_startup_grace_until = Some(now + AGENT_STARTUP_GRACE_WINDOW); - baseline_pty_causality( - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq.load(Ordering::Relaxed), - ); state = AgentState::Idle; last_visible_idle = true; last_visible_blocker = false; @@ -583,8 +565,6 @@ fn spawn_basic_detection_task( if full_lifecycle_authority_active.load(Ordering::Acquire) && !process_exited { pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); continue; } @@ -592,36 +572,20 @@ fn spawn_basic_detection_task( if process_exited { agent_startup_grace_until = None; pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); } else { if now < until { pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); continue; } - baseline_pty_causality( - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq.load(Ordering::Relaxed), - ); agent_startup_grace_until = None; - last_screen_scan_pty_output_seq = None; + last_screen_scan_detection_content_seq = None; pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); continue; } } - let pty_activity = if agent.is_some() { - Some(agent_caused_pty_activity_active( - pty_output_seq.load(Ordering::Relaxed), - input_write_seq.load(Ordering::Relaxed), - &mut pty_causality, - now, - )) + let current_detection_content_seq = if agent.is_some() { + Some(detection_content_seq.load(Ordering::Relaxed)) } else { None }; @@ -629,33 +593,21 @@ fn spawn_basic_detection_task( state, agent, pending_idle_active: pending_idle.active(), - pending_working_active: pending_working.active(), - post_taint_working_active: post_taint_working.active(), agent_changed, process_exited, - pty_activity, - last_screen_scan_pty_output_seq, + current_detection_content_seq, + last_screen_scan_detection_content_seq, }) { DetectionScreenReadDecision::Read => {} DetectionScreenReadDecision::Skip => continue, } let content = terminal.detection_text(); - last_screen_scan_pty_output_seq = pty_activity.map(|signal| signal.output_seq); + last_screen_scan_detection_content_seq = current_detection_content_seq; let content_changed = content != last_detection_text; last_detection_text.clone_from(&content); if !process_exited && crate::detect::should_skip_state_update(agent, &content) { - handle_skipped_detection_update( - state, - pty_activity, - &mut post_taint_working, - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq.load(Ordering::Relaxed), - now, - ); pending_idle.clear(); - pending_working.clear(); continue; } sync_content_change_acquisition( @@ -677,17 +629,7 @@ fn spawn_basic_detection_task( &osc_progress, process_exited, ) else { - handle_skipped_detection_update( - state, - pty_activity, - &mut post_taint_working, - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq.load(Ordering::Relaxed), - now, - ); pending_idle.clear(); - pending_working.clear(); continue; }; match decide_screen_detection_publish( @@ -700,12 +642,9 @@ fn spawn_basic_detection_task( last_visible_signal_refresh, process_exited, agent_changed, - pty_activity, now, }, &mut pending_idle, - &mut pending_working, - &mut post_taint_working, ) { DetectionPublishDecision::NoPublish => {} DetectionPublishDecision::Publish { @@ -807,7 +746,7 @@ pub struct PaneRuntime { reported_cwd: Arc<Mutex<Option<std::path::PathBuf>>>, child_wait_completed: Option<Arc<AtomicBool>>, kitty_keyboard_flags: Arc<AtomicU16>, - input_write_seq: Arc<AtomicU64>, + detection_content_seq: Arc<AtomicU64>, full_lifecycle_authority_active: Arc<AtomicBool>, detect_reset_notify: Arc<Notify>, pending_release: Arc<Mutex<Option<PendingAgentRelease>>>, @@ -1551,25 +1490,24 @@ impl PaneRuntime { let child_pid = Arc::new(AtomicU32::new(child_pid)); let reported_cwd = Arc::new(Mutex::new(None)); let kitty_keyboard_flags = Arc::new(AtomicU16::new(keyboard_protocol_flags)); - let input_write_seq = Arc::new(AtomicU64::new(0)); - let pty_output_seq = Arc::new(AtomicU64::new(0)); + let detection_content_seq = Arc::new(AtomicU64::new(0)); let io = { let terminal = terminal.clone(); let response_writer = response_tx.clone(); let render_notify = render_notify.clone(); let render_dirty = render_dirty.clone(); - let pty_output_seq = pty_output_seq.clone(); + let detection_content_seq = detection_content_seq.clone(); let child_pid = child_pid.clone(); let read_events = events.clone(); let reported_cwd = reported_cwd.clone(); let rt = tokio::runtime::Handle::current(); let delay_rt = rt.clone(); let on_read = Box::new(move |bytes: &[u8]| { - observe_pty_output_activity(bytes, &pty_output_seq); let shell_pid = child_pid.load(Ordering::Acquire); let result = terminal.process_pty_bytes(pane_id, shell_pid, bytes, &response_writer); + observe_detection_content_change(bytes, &detection_content_seq); if result.request_render && !render_dirty.swap(true, Ordering::AcqRel) { render_notify.notify_one(); } @@ -1618,8 +1556,7 @@ impl PaneRuntime { pane_id, child_pid.clone(), terminal.clone(), - pty_output_seq, - input_write_seq.clone(), + detection_content_seq.clone(), full_lifecycle_authority_active.clone(), events, ); @@ -1633,7 +1570,7 @@ impl PaneRuntime { reported_cwd, child_wait_completed: None, kitty_keyboard_flags, - input_write_seq, + detection_content_seq, full_lifecycle_authority_active, detect_reset_notify, pending_release, @@ -1683,8 +1620,7 @@ impl PaneRuntime { let child_pid = Arc::new(AtomicU32::new(0)); let reported_cwd = Arc::new(Mutex::new(None)); let child_wait_completed = Arc::new(AtomicBool::new(false)); - let input_write_seq = Arc::new(AtomicU64::new(0)); - let pty_output_seq = Arc::new(AtomicU64::new(0)); + let detection_content_seq = Arc::new(AtomicU64::new(0)); let full_lifecycle_authority_active = Arc::new(AtomicBool::new(false)); { let child_pid = child_pid.clone(); @@ -1717,16 +1653,16 @@ impl PaneRuntime { let response_writer = response_tx.clone(); let render_notify = render_notify.clone(); let render_dirty = render_dirty.clone(); - let pty_output_seq = pty_output_seq.clone(); + let detection_content_seq = detection_content_seq.clone(); let child_pid = child_pid.clone(); let events = events.clone(); let reported_cwd = reported_cwd.clone(); let rt = tokio::runtime::Handle::current(); let on_read = Box::new(move |bytes: &[u8]| { - observe_pty_output_activity(bytes, &pty_output_seq); let shell_pid = child_pid.load(Ordering::Acquire); let result = terminal.process_pty_bytes(pane_id, shell_pid, bytes, &response_writer); + observe_detection_content_change(bytes, &detection_content_seq); if result.request_render && !render_dirty.swap(true, Ordering::AcqRel) { render_notify.notify_one(); } @@ -1780,8 +1716,7 @@ impl PaneRuntime { let child_pid = child_pid.clone(); let terminal = terminal.clone(); let state_events = events.clone(); - let pty_output_seq = pty_output_seq.clone(); - let input_write_seq_for_task = input_write_seq.clone(); + let detection_content_seq = detection_content_seq.clone(); let full_lifecycle_authority_active_for_task = full_lifecycle_authority_active.clone(); let render_notify = render_notify.clone(); let render_dirty = render_dirty.clone(); @@ -1808,12 +1743,9 @@ impl PaneRuntime { let mut last_visible_working = false; let mut last_visible_signal_refresh = None; let mut last_detection_text = String::new(); - let mut last_screen_scan_pty_output_seq = None; - let mut pty_causality = PtyCausalityTracker::default(); + let mut last_screen_scan_detection_content_seq = None; let mut agent_startup_grace_until = None; let mut pending_idle = PendingIdleConfirmation::default(); - let mut pending_working = PendingWorkingConfirmation::default(); - let mut post_taint_working = PostTaintWorkingLease::default(); tokio::time::sleep(Duration::from_millis(50)).await; @@ -1824,8 +1756,6 @@ impl PaneRuntime { || terminal.has_transient_default_color_override() { TICK_PENDING_RELEASE - } else if pending_working.active() { - pending_working.recheck_delay(now_for_tick) } else if pending_idle.active() { AGENT_PENDING_IDLE_RECHECK } else if agent_presence.current_agent().is_none() { @@ -1851,12 +1781,9 @@ impl PaneRuntime { last_visible_working = false; last_visible_signal_refresh = None; last_detection_text.clear(); - last_screen_scan_pty_output_seq = None; - pty_causality = PtyCausalityTracker::default(); + last_screen_scan_detection_content_seq = None; agent_startup_grace_until = None; pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); } } @@ -1951,20 +1878,13 @@ impl PaneRuntime { agent = agent_presence.current_agent(); if agent != previous_agent { pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); - last_screen_scan_pty_output_seq = None; + last_screen_scan_detection_content_seq = None; // A new foreground agent must not inherit OSC // title/progress evidence from the previous process. terminal.clear_agent_osc_state(); if agent.is_some() { agent_startup_grace_until = Some(now + AGENT_STARTUP_GRACE_WINDOW); - baseline_pty_causality( - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq_for_task.load(Ordering::Relaxed), - ); state = AgentState::Idle; last_visible_idle = true; last_visible_blocker = false; @@ -2025,45 +1945,27 @@ impl PaneRuntime { && !process_exited { pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); continue; } if let Some(until) = agent_startup_grace_until { if process_exited { agent_startup_grace_until = None; - last_screen_scan_pty_output_seq = None; + last_screen_scan_detection_content_seq = None; pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); } else { if now < until { pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); continue; } - baseline_pty_causality( - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq_for_task.load(Ordering::Relaxed), - ); agent_startup_grace_until = None; pending_idle.clear(); - pending_working.clear(); - post_taint_working.clear(); continue; } } - let pty_activity = if agent.is_some() { - Some(agent_caused_pty_activity_active( - pty_output_seq.load(Ordering::Relaxed), - input_write_seq_for_task.load(Ordering::Relaxed), - &mut pty_causality, - now, - )) + let current_detection_content_seq = if agent.is_some() { + Some(detection_content_seq.load(Ordering::Relaxed)) } else { None }; @@ -2071,33 +1973,21 @@ impl PaneRuntime { state, agent, pending_idle_active: pending_idle.active(), - pending_working_active: pending_working.active(), - post_taint_working_active: post_taint_working.active(), agent_changed, process_exited, - pty_activity, - last_screen_scan_pty_output_seq, + current_detection_content_seq, + last_screen_scan_detection_content_seq, }) { DetectionScreenReadDecision::Read => {} DetectionScreenReadDecision::Skip => continue, } let content = terminal.detection_text(); - last_screen_scan_pty_output_seq = pty_activity.map(|signal| signal.output_seq); + last_screen_scan_detection_content_seq = current_detection_content_seq; let content_changed = content != last_detection_text; last_detection_text.clone_from(&content); if detect::should_skip_state_update(agent, &content) { - handle_skipped_detection_update( - state, - pty_activity, - &mut post_taint_working, - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq_for_task.load(Ordering::Relaxed), - now, - ); pending_idle.clear(); - pending_working.clear(); continue; } sync_content_change_acquisition( @@ -2119,17 +2009,7 @@ impl PaneRuntime { &osc_progress, process_exited, ) else { - handle_skipped_detection_update( - state, - pty_activity, - &mut post_taint_working, - &mut pty_causality, - pty_output_seq.load(Ordering::Relaxed), - input_write_seq_for_task.load(Ordering::Relaxed), - now, - ); pending_idle.clear(); - pending_working.clear(); continue; }; match decide_screen_detection_publish( @@ -2142,12 +2022,9 @@ impl PaneRuntime { last_visible_signal_refresh, process_exited, agent_changed, - pty_activity, now, }, &mut pending_idle, - &mut pending_working, - &mut post_taint_working, ) { DetectionPublishDecision::NoPublish => {} DetectionPublishDecision::Publish { @@ -2193,7 +2070,7 @@ impl PaneRuntime { reported_cwd, child_wait_completed: Some(child_wait_completed), kitty_keyboard_flags, - input_write_seq, + detection_content_seq, full_lifecycle_authority_active, detect_reset_notify, pending_release, @@ -2242,11 +2119,11 @@ impl PaneRuntime { if self.current_size.get() == size { return; } - self.input_write_seq.fetch_add(1, Ordering::Relaxed); self.current_size.set(size); let terminal_responses = self .terminal .resize(rows, cols, cell_width_px, cell_height_px); + mark_detection_content_changed(&self.detection_content_seq); self.io.resize( rows, cols, @@ -2258,7 +2135,6 @@ impl PaneRuntime { #[cfg(unix)] pub fn nudge_child_redraw_after_handoff(&self) { - self.input_write_seq.fetch_add(1, Ordering::Relaxed); let (rows, cols, cell_width_px, cell_height_px) = self.current_size.get(); self.io .nudge_child_redraw_after_handoff(rows, cols, cell_width_px, cell_height_px); @@ -2397,19 +2273,11 @@ impl PaneRuntime { } pub async fn send_bytes(&self, bytes: Bytes) -> Result<(), mpsc::error::SendError<Bytes>> { - let result = self.io.send_bytes(bytes).await; - if result.is_ok() { - self.input_write_seq.fetch_add(1, Ordering::Relaxed); - } - result + self.io.send_bytes(bytes).await } pub fn try_send_bytes(&self, bytes: Bytes) -> Result<(), mpsc::error::TrySendError<Bytes>> { - let result = self.io.try_send_bytes(bytes); - if result.is_ok() { - self.input_write_seq.fetch_add(1, Ordering::Relaxed); - } - result + self.io.try_send_bytes(bytes) } pub async fn send_paste(&self, text: String) -> Result<(), mpsc::error::SendError<Bytes>> { @@ -2607,7 +2475,7 @@ impl PaneRuntime { reported_cwd: Arc::new(Mutex::new(None)), child_wait_completed: None, kitty_keyboard_flags: Arc::new(AtomicU16::new(0)), - input_write_seq: Arc::new(AtomicU64::new(0)), + detection_content_seq: Arc::new(AtomicU64::new(0)), full_lifecycle_authority_active: Arc::new(AtomicBool::new(false)), detect_reset_notify: Arc::new(Notify::new()), pending_release: Arc::new(Mutex::new(None)), @@ -2981,7 +2849,7 @@ mod tests { reported_cwd: Arc::new(Mutex::new(None)), child_wait_completed: None, kitty_keyboard_flags: Arc::new(AtomicU16::new(0)), - input_write_seq: Arc::new(AtomicU64::new(0)), + detection_content_seq: Arc::new(AtomicU64::new(0)), full_lifecycle_authority_active: Arc::new(AtomicBool::new(false)), detect_reset_notify: Arc::new(Notify::new()), pending_release: Arc::new(Mutex::new(None)), @@ -3012,7 +2880,7 @@ mod tests { reported_cwd: Arc::new(Mutex::new(None)), child_wait_completed: None, kitty_keyboard_flags: Arc::new(AtomicU16::new(0)), - input_write_seq: Arc::new(AtomicU64::new(0)), + detection_content_seq: Arc::new(AtomicU64::new(0)), full_lifecycle_authority_active: Arc::new(AtomicBool::new(false)), detect_reset_notify: Arc::new(Notify::new()), pending_release: Arc::new(Mutex::new(None)), |
