aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-09-22 02:20:47 +0300
committerGitHub <noreply@github.com>2026-09-22 02:20:47 +0300
commit8ac9542757292f7a8d42a2d532bc6a8a33c7ffce (patch)
tree76a89e2ef69ae8725bba0bcec5f635c181de0134 /src
parent28360107c1bfab57fb0de99d74bfa48e74423ca8 (diff)
fix: clear inherited terminal and agent session identity (#4461)
refs #4104
Diffstat (limited to 'src')
-rw-r--r--src/pane.rs110
1 files changed, 94 insertions, 16 deletions
diff --git a/src/pane.rs b/src/pane.rs
index 485b226c..9d75c21c 100644
--- a/src/pane.rs
+++ b/src/pane.rs
@@ -96,7 +96,25 @@ fn apply_pane_terminal_env(cmd: &mut CommandBuilder) {
// when the remote side lacks matching terminfo entries.
cmd.env("TERM", PANE_TERM);
cmd.env("COLORTERM", PANE_COLORTERM);
- cmd.env_remove("WT_SESSION");
+ cmd.env("TERM_PROGRAM", "herdr");
+ cmd.env("TERM_PROGRAM_VERSION", crate::build_info::version());
+ // Host handles refer to the outer terminal, never to this pane.
+ for key in [
+ "ITERM_SESSION_ID",
+ "LC_TERMINAL",
+ "LC_TERMINAL_VERSION",
+ "WEZTERM_PANE",
+ "KITTY_WINDOW_ID",
+ "WT_SESSION",
+ "TMUX",
+ "TMUX_PANE",
+ "STY",
+ "ZELLIJ",
+ "ZELLIJ_SESSION_NAME",
+ "ZELLIJ_PANE_ID",
+ ] {
+ cmd.env_remove(key);
+ }
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
@@ -148,10 +166,18 @@ impl PaneLaunchEnv {
fn apply_pane_launch_env(cmd: &mut CommandBuilder, launch_env: &PaneLaunchEnv) {
#[cfg(unix)]
crate::platform::ssh_agent::apply_pane_env(cmd);
- cmd.env_remove("CODEX_THREAD_ID");
- // OMP sets OMPCODE for shells it spawns. A pane launched from inside OMP
- // must not inherit it or its root agent would look like a nested session.
- cmd.env_remove("OMPCODE");
+ // A new pane is not a child agent of the process that started the server.
+ // Explicit launch env below can opt back into an intentional child session.
+ for key in [
+ "CODEX_THREAD_ID",
+ "OMPCODE",
+ "CLAUDECODE",
+ "CLAUDE_CODE_CHILD_SESSION",
+ "CLAUDE_CODE_SESSION_ID",
+ "CLAUDE_CODE_MESSAGING_TOKEN",
+ ] {
+ cmd.env_remove(key);
+ }
for (key, value) in &launch_env.extra {
cmd.env(key, value);
}
@@ -3715,6 +3741,7 @@ impl PaneRuntime {
#[cfg(test)]
mod tests {
use super::*;
+ use std::ffi::OsStr;
#[tokio::test]
async fn clear_pane_preserves_wrapped_input_and_unfinished_vt_sequence() {
@@ -3824,33 +3851,84 @@ mod tests {
}
#[test]
- fn pane_launch_env_removes_outer_codex_thread_id() {
+ fn pane_launch_env_removes_outer_agent_identity() {
+ let keys = [
+ "CODEX_THREAD_ID",
+ "OMPCODE",
+ "CLAUDECODE",
+ "CLAUDE_CODE_CHILD_SESSION",
+ "CLAUDE_CODE_SESSION_ID",
+ "CLAUDE_CODE_MESSAGING_TOKEN",
+ ];
let mut cmd = CommandBuilder::new("shell");
- cmd.env("CODEX_THREAD_ID", "outer-session");
+ for key in keys {
+ cmd.env(key, "outer-session");
+ }
+ cmd.env("ANTHROPIC_API_KEY", "fake-api-key");
+ cmd.env("DISPLAY", ":42");
apply_pane_launch_env(&mut cmd, &PaneLaunchEnv::default());
- assert!(cmd.get_env("CODEX_THREAD_ID").is_none());
+ for key in keys {
+ assert!(cmd.get_env(key).is_none(), "{key} must not leak into panes");
+ }
+ assert_eq!(
+ cmd.get_env("ANTHROPIC_API_KEY"),
+ Some(OsStr::new("fake-api-key"))
+ );
+ assert_eq!(cmd.get_env("DISPLAY"), Some(OsStr::new(":42")));
}
#[test]
- fn pane_launch_env_removes_outer_ompcode_marker() {
+ fn pane_terminal_identity_removes_outer_terminal_identity() {
+ let keys = [
+ "ITERM_SESSION_ID",
+ "LC_TERMINAL",
+ "LC_TERMINAL_VERSION",
+ "WEZTERM_PANE",
+ "KITTY_WINDOW_ID",
+ "WT_SESSION",
+ "TMUX",
+ "TMUX_PANE",
+ "STY",
+ "ZELLIJ",
+ "ZELLIJ_SESSION_NAME",
+ "ZELLIJ_PANE_ID",
+ ];
let mut cmd = CommandBuilder::new("shell");
- cmd.env("OMPCODE", "1");
+ for key in keys {
+ cmd.env(key, "outer-session");
+ }
+ cmd.env("TERM_PROGRAM", "iTerm.app");
+ cmd.env("TERM_PROGRAM_VERSION", "outer-version");
- apply_pane_launch_env(&mut cmd, &PaneLaunchEnv::default());
+ apply_pane_terminal_env(&mut cmd);
- assert!(cmd.get_env("OMPCODE").is_none());
+ for key in keys {
+ assert!(cmd.get_env(key).is_none(), "{key} must not leak into panes");
+ }
+ assert_eq!(cmd.get_env("TERM_PROGRAM"), Some(OsStr::new("herdr")));
+ assert_eq!(
+ cmd.get_env("TERM_PROGRAM_VERSION"),
+ Some(OsStr::new(&crate::build_info::version()))
+ );
}
#[test]
- fn pane_terminal_identity_removes_outer_windows_terminal_session() {
+ fn pane_launch_env_allows_explicit_session_identity() {
+ let extra = vec![
+ ("CLAUDE_CODE_CHILD_SESSION".into(), "1".into()),
+ ("CLAUDE_CODE_SESSION_ID".into(), "intentional-child".into()),
+ ("CLAUDE_CODE_MESSAGING_TOKEN".into(), "fake-token".into()),
+ ("ITERM_SESSION_ID".into(), "intentional-host".into()),
+ ];
let mut cmd = CommandBuilder::new("shell");
- cmd.env("WT_SESSION", "outer-session");
-
apply_pane_terminal_env(&mut cmd);
+ apply_pane_launch_env(&mut cmd, &PaneLaunchEnv::from_extra(extra.clone()));
- assert!(cmd.get_env("WT_SESSION").is_none());
+ for (key, value) in extra {
+ assert_eq!(cmd.get_env(key), Some(OsStr::new(&value)));
+ }
}
#[tokio::test]