fix: harden codex runs against tuleap mcp runtime panic

This commit is contained in:
thibaud-lclr 2026-04-16 16:28:25 +02:00
parent 33439e11c8
commit 906c44ef22
2 changed files with 42 additions and 4 deletions

View file

@ -67,7 +67,13 @@ impl AgentTool {
pub fn to_non_interactive_args(&self) -> Vec<String> {
match self {
AgentTool::Codex => vec!["exec".to_string(), "-".to_string()],
AgentTool::Codex => vec![
"exec".to_string(),
"--ephemeral".to_string(),
"-c".to_string(),
"mcp_servers.tuleap.enabled=false".to_string(),
"-".to_string(),
],
AgentTool::ClaudeCode => vec!["-p".to_string()],
}
}
@ -299,7 +305,13 @@ mod tests {
fn test_non_interactive_args_match_cli_expectations() {
assert_eq!(
AgentTool::Codex.to_non_interactive_args(),
vec!["exec".to_string(), "-".to_string()]
vec![
"exec".to_string(),
"--ephemeral".to_string(),
"-c".to_string(),
"mcp_servers.tuleap.enabled=false".to_string(),
"-".to_string()
]
);
assert_eq!(
AgentTool::ClaudeCode.to_non_interactive_args(),

View file

@ -2,6 +2,19 @@ use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::process::Command;
use tokio::time::{timeout, Duration};
fn normalize_process_stderr(stderr: &str) -> String {
let trimmed = stderr.trim();
if trimmed.is_empty() {
return String::new();
}
if trimmed.contains("Cannot start a runtime from within a runtime") {
return "Le processus agent a plante avec un conflit de runtime Tokio. Orchai desactive MCP Tuleap sur les executions automatisees Codex ; relancez la tache. Si l'erreur persiste, mettez a jour ou desactivez le binaire local tuleap-mcp.".to_string();
}
trimmed.to_string()
}
pub async fn run_agent_command(
command: &str,
args: &[String],
@ -31,7 +44,7 @@ pub async fn run_agent_command(
.map_err(|e| format!("Failed to wait process output: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
let stderr = normalize_process_stderr(&String::from_utf8_lossy(&output.stderr));
let code = output.status.code().unwrap_or(-1);
if stderr.is_empty() {
return Err(format!("CLI command exited with code {}", code));
@ -118,7 +131,7 @@ where
.map_err(|_| format!("CLI command timed out after {}s", timeout_secs))??;
if !status.success() {
let stderr = stderr_output.trim().to_string();
let stderr = normalize_process_stderr(&stderr_output);
let code = status.code().unwrap_or(-1);
if stderr.is_empty() {
return Err(format!("CLI command exited with code {}", code));
@ -138,6 +151,19 @@ where
mod tests {
use super::*;
#[test]
fn test_normalize_process_stderr_runtime_panic() {
let raw = "thread 'tokio-rt-worker' panicked\nCannot start a runtime from within a runtime";
let normalized = normalize_process_stderr(raw);
assert!(normalized.contains("conflit de runtime Tokio"));
}
#[test]
fn test_normalize_process_stderr_passthrough() {
let raw = "some other stderr";
assert_eq!(normalize_process_stderr(raw), "some other stderr");
}
#[tokio::test]
async fn test_run_agent_command_streaming_collects_chunks() {
let args = vec!["-c".to_string(), "cat".to_string()];