From 18909f0aef3a6f9987bff849b3f7e5b1efa7e71f Mon Sep 17 00:00:00 2001 From: jif-oai Date: Mon, 8 Dec 2025 16:41:04 +0000 Subject: [PATCH] make 1 --- .../core/src/unified_exec/session_manager.rs | 53 ++++++++++++++++++- codex-rs/core/tests/suite/unified_exec.rs | 10 ++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/codex-rs/core/src/unified_exec/session_manager.rs b/codex-rs/core/src/unified_exec/session_manager.rs index 88d65ca142..b56cfbdd86 100644 --- a/codex-rs/core/src/unified_exec/session_manager.rs +++ b/codex-rs/core/src/unified_exec/session_manager.rs @@ -62,6 +62,53 @@ const UNIFIED_EXEC_ENV: [(&str, &str); 8] = [ ("GIT_PAGER", "cat"), ]; +fn normalize_unified_exec_text(raw: &str) -> String { + let with_unix_newlines = raw.replace("\r\n", "\n"); + let with_unix_newlines = with_unix_newlines.replace('\r', "\n"); + strip_ansi_escape_sequences(&with_unix_newlines) +} + +fn strip_ansi_escape_sequences(input: &str) -> String { + let mut result = String::with_capacity(input.len()); + let mut iter = input.chars().peekable(); + + while let Some(ch) = iter.next() { + if ch == '\u{1b}' { + match iter.peek().copied() { + Some('[') => { + let _ = iter.next(); + for c in iter.by_ref() { + if ('@'..='~').contains(&c) { + break; + } + } + } + Some(']') => { + let _ = iter.next(); + while let Some(c) = iter.next() { + if c == '\u{7}' { + break; + } + if c == '\u{1b}' + && let Some('\\') = iter.peek().copied() { + let _ = iter.next(); + break; + } + } + } + Some(_) => { + let _ = iter.next(); + } + None => {} + } + } else { + result.push(ch); + } + } + + result +} + fn apply_unified_exec_env(mut env: HashMap) -> HashMap { for (key, value) in UNIFIED_EXEC_ENV { env.insert(key.to_string(), value.to_string()); @@ -163,7 +210,8 @@ impl UnifiedExecSessionManager { .await; let wall_time = Instant::now().saturating_duration_since(start); - let text = String::from_utf8_lossy(&collected).to_string(); + let raw_text = String::from_utf8_lossy(&collected).to_string(); + let text = normalize_unified_exec_text(&raw_text); let output = formatted_truncate_text(&text, TruncationPolicy::Tokens(max_tokens)); let has_exited = session.has_exited(); let exit_code = session.exit_code(); @@ -285,7 +333,8 @@ impl UnifiedExecSessionManager { .await; let wall_time = Instant::now().saturating_duration_since(start); - let text = String::from_utf8_lossy(&collected).to_string(); + let raw_text = String::from_utf8_lossy(&collected).to_string(); + let text = normalize_unified_exec_text(&raw_text); let output = formatted_truncate_text(&text, TruncationPolicy::Tokens(max_tokens)); let original_token_count = approx_token_count(&text); let chunk_id = generate_chunk_id(); diff --git a/codex-rs/core/tests/suite/unified_exec.rs b/codex-rs/core/tests/suite/unified_exec.rs index 79842d2425..852f735fe4 100644 --- a/codex-rs/core/tests/suite/unified_exec.rs +++ b/codex-rs/core/tests/suite/unified_exec.rs @@ -2390,6 +2390,15 @@ async fn windows_unified_exec_escape_output_snapshot() -> Result<()> { let raw_output = parsed.output.as_str(); + // assert!( + // raw_output.contains("UEXEC-WINDOWS-ESCAPES"), + // "expected marker string in unified_exec output, got {raw_output:?}" + // ); + // assert!( + // !raw_output.contains('\u{1b}'), + // "expected unified_exec output to be stripped of ANSI escape sequences on Windows, got {raw_output:?}" + // ); + // Intentionally failing assertion so CI logs show the raw PTY output, // including any escape sequences produced by ConPTY / PowerShell. let expected = "UEXEC-WINDOWS-ESCAPES\n"; @@ -2397,6 +2406,7 @@ async fn windows_unified_exec_escape_output_snapshot() -> Result<()> { raw_output, expected, "raw unified_exec PTY output on Windows (expected simple marker, got full stream)" ); + assert!(false); Ok(()) }