diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-01 23:31:53 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-01 23:32:44 +0300 |
| commit | 36a4608603a82dbf32ec8a013dd47047cf02c6a3 (patch) | |
| tree | 8a9242974ad45dd6dc22b66f576dd9adcc36d996 /src/pane.rs | |
| parent | 5bd21b7ed468b5ff4d1a5b2ed7e211f401eb138a (diff) | |
fix: restore agents through pane shells
Diffstat (limited to 'src/pane.rs')
| -rw-r--r-- | src/pane.rs | 155 |
1 files changed, 0 insertions, 155 deletions
diff --git a/src/pane.rs b/src/pane.rs index 94daebc0..35b2318e 100644 --- a/src/pane.rs +++ b/src/pane.rs @@ -1149,51 +1149,6 @@ impl PaneRuntime { ) } - pub fn spawn_agent_restore( - pane_id: PaneId, - rows: u16, - cols: u16, - cwd: std::path::PathBuf, - launch: crate::agent_resume::AgentResumeLaunch<'_>, - scrollback_limit_bytes: usize, - host_terminal_theme: crate::terminal_theme::TerminalTheme, - events: mpsc::Sender<AppEvent>, - render_notify: Arc<Notify>, - render_dirty: Arc<AtomicBool>, - ) -> std::io::Result<Self> { - let Some((program, args)) = launch.plan.argv.split_first() else { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "restore argv must not be empty", - )); - }; - - let mut cmd = CommandBuilder::new(program); - for arg in args { - cmd.arg(arg); - } - cmd.cwd(cwd); - cmd.env(crate::HERDR_ENV_VAR, crate::HERDR_ENV_VALUE); - apply_pane_terminal_env(&mut cmd); - crate::integration::apply_pane_env(&mut cmd, pane_id); - Self::spawn_command_builder( - pane_id, - rows, - cols, - scrollback_limit_bytes, - host_terminal_theme, - events, - render_notify, - render_dirty, - cmd, - "failed to spawn agent restore pane", - SpawnInitialState { - detected_agent: crate::detect::parse_agent_label(&launch.plan.agent), - history_ansi: launch.initial_history_ansi, - }, - ) - } - #[cfg(unix)] pub fn from_handoff_fd( import: crate::handoff_runtime::ImportedHandoffRuntime, @@ -2359,116 +2314,6 @@ mod tests { assert!(truncated.is_empty()); } - fn process_command_name(pid: u32) -> Option<String> { - let output = std::process::Command::new("ps") - .args(["-p", &pid.to_string(), "-o", "comm="]) - .output() - .ok()?; - if !output.status.success() { - return None; - } - let command = String::from_utf8_lossy(&output.stdout).trim().to_string(); - (!command.is_empty()).then_some(command) - } - - async fn wait_for_child_pid(runtime: &PaneRuntime) -> u32 { - let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(2); - while tokio::time::Instant::now() < deadline { - let pid = runtime.child_pid.load(Ordering::Acquire); - if pid != 0 { - return pid; - } - tokio::time::sleep(std::time::Duration::from_millis(10)).await; - } - panic!("child pid was not published"); - } - - #[tokio::test] - async fn spawn_agent_restore_uses_restore_command_as_pane_child() { - let (events, _event_rx) = mpsc::channel(4); - let plan = crate::agent_resume::AgentResumePlan { - agent: "codex".into(), - argv: vec!["/bin/cat".into()], - dedupe_key: "test".into(), - }; - let runtime = PaneRuntime::spawn_agent_restore( - PaneId::from_raw(7), - 24, - 80, - std::env::current_dir().unwrap(), - crate::agent_resume::AgentResumeLaunch { - plan: &plan, - initial_history_ansi: None, - }, - 0, - crate::terminal_theme::TerminalTheme::default(), - events, - Arc::new(Notify::new()), - Arc::new(AtomicBool::new(false)), - ) - .unwrap(); - - let pid = wait_for_child_pid(&runtime).await; - let command = process_command_name(pid).expect("child process should be visible to ps"); - - assert!( - command.ends_with("cat"), - "restore command should be the pane child, got {command:?}" - ); - assert!( - !command.ends_with("sh"), - "restore must not keep a shell wrapper as the pane child" - ); - - runtime.shutdown(); - } - - #[tokio::test] - async fn spawn_agent_restore_reports_pane_death_after_early_failure() { - let (events, mut event_rx) = mpsc::channel(8); - let plan = crate::agent_resume::AgentResumePlan { - agent: "codex".into(), - argv: vec!["/bin/sh".into(), "-c".into(), "exit 7".into()], - dedupe_key: "test".into(), - }; - let runtime = PaneRuntime::spawn_agent_restore( - PaneId::from_raw(7), - 24, - 80, - std::env::current_dir().unwrap(), - crate::agent_resume::AgentResumeLaunch { - plan: &plan, - initial_history_ansi: None, - }, - 0, - crate::terminal_theme::TerminalTheme::default(), - events, - Arc::new(Notify::new()), - Arc::new(AtomicBool::new(false)), - ) - .unwrap(); - - let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(2); - let mut died = false; - while tokio::time::Instant::now() < deadline { - let Some(event) = tokio::time::timeout( - deadline.saturating_duration_since(tokio::time::Instant::now()), - event_rx.recv(), - ) - .await - .expect("pane death event should arrive") else { - break; - }; - if matches!(event, AppEvent::PaneDied { pane_id } if pane_id == PaneId::from_raw(7)) { - died = true; - break; - } - } - - assert!(died, "failed direct agent restore should report pane death"); - runtime.shutdown(); - } - #[tokio::test] async fn focus_events_are_forwarded_when_enabled() { let (tx, mut rx) = mpsc::channel(4); |
