From 2f20fdbab4add9bfa9259302d1eea2c993567c3f Mon Sep 17 00:00:00 2001 From: Blomios Date: Wed, 10 Jun 2026 23:15:36 +0200 Subject: [PATCH] =?UTF-8?q?fix(agent):=20retrait=20--ask-for-approval=20in?= =?UTF-8?q?valide=20pour=20codex=20exec=200.137=20=E2=80=94=20=C2=A717.9?= =?UTF-8?q?=20D3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `codex exec` 0.137 ne connaît pas `--ask-for-approval` (`error: unexpected argument`) ; ce flag appartient à la commande interactive `codex`, pas à la sous-commande `exec` déjà non-interactive. Sa présence cassait toute réponse d'un agent Codex (flux structuré terminé sans Final), bloquant la conversation inter-agents. argv désormais verrouillé exact dans les tests (16/16 verts). Découvert et corrigé pendant la validation réelle de la messagerie inter-agents. Co-Authored-By: Claude Opus 4.8 --- crates/infrastructure/src/session/codex.rs | 21 +++-- crates/infrastructure/src/session/mod.rs | 90 ++++++++++------------ 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/crates/infrastructure/src/session/codex.rs b/crates/infrastructure/src/session/codex.rs index 8e329f4..b3c683b 100644 --- a/crates/infrastructure/src/session/codex.rs +++ b/crates/infrastructure/src/session/codex.rs @@ -141,19 +141,20 @@ impl CodexExecSession { /// Compose la ligne de commande d'un tour. /// - /// Format RÉEL vérifié 2026-06-09 : + /// Format RÉEL vérifié 2026-06-10 (codex 0.137.0) : /// - Conversation neuve : `codex exec --json --skip-git-repo-check - /// --sandbox workspace-write --ask-for-approval never `. + /// --sandbox workspace-write `. /// - Reprise (id connu) : `codex exec resume --json - /// --skip-git-repo-check --sandbox workspace-write --ask-for-approval never - /// `. + /// --skip-git-repo-check --sandbox workspace-write `. /// /// **Autonomie d'écriture (D3)** : `--sandbox workspace-write` autorise l'agent à - /// écrire dans son workspace et `--ask-for-approval never` évite tout blocage sur - /// une demande d'approbation interactive (un tour structuré non-interactif ne peut - /// répondre à un prompt). Défaut raisonnable, aligné sur l'autonomie projet - /// (CLAUDE.md §12) ; à terme **piloté par les permissions de l'agent** - /// (`.ideai/permissions.json` + sandbox OS) — non implémenté ici. + /// écrire dans son workspace. `codex exec` est déjà non-interactif (aucun prompt + /// d'approbation possible), donc on ne passe **pas** `--ask-for-approval` : ce flag + /// appartient à la commande interactive `codex`, pas à la sous-commande `exec` qui + /// sort sur `error: unexpected argument '--ask-for-approval' found`. Défaut + /// raisonnable, aligné sur l'autonomie projet (CLAUDE.md §12) ; à terme **piloté par + /// les permissions de l'agent** (`.ideai/permissions.json` + sandbox OS) — non + /// implémenté ici. fn build_spawn_line(&self, prompt: &str) -> SpawnLine { let mut args = vec!["exec".to_owned()]; if let Some(id) = self.conversation_id.lock().expect("mutex sain").as_ref() { @@ -164,8 +165,6 @@ impl CodexExecSession { args.push("--skip-git-repo-check".to_owned()); args.push("--sandbox".to_owned()); args.push("workspace-write".to_owned()); - args.push("--ask-for-approval".to_owned()); - args.push("never".to_owned()); args.push(prompt.to_owned()); SpawnLine { command: self.command.clone(), diff --git a/crates/infrastructure/src/session/mod.rs b/crates/infrastructure/src/session/mod.rs index b978c04..de4084d 100644 --- a/crates/infrastructure/src/session/mod.rs +++ b/crates/infrastructure/src/session/mod.rs @@ -1166,18 +1166,21 @@ mod tests { } // ===================================================================== - // DURCISSEMENT QA (lot D3, §17.9 D3) — autonomie d'écriture Codex : - // la commande générée porte `--sandbox workspace-write` ET - // `--ask-for-approval never`, conversation NEUVE comme en REPRISE. + // DURCISSEMENT QA (lot D3, §17.9 D3 — fix codex 0.137) — autonomie + // d'écriture Codex : la commande générée porte EXACTEMENT + // [exec, --json, --skip-git-repo-check, --sandbox, workspace-write, ] + // (resume en tête pour une reprise). Le flag `--ask-for-approval never` + // a été RETIRÉ : `codex exec` 0.137 ne le connaît pas (`error: unexpected + // argument`) et est déjà non-interactif. Ce test verrouille l'argv exact pour + // qu'aucune régression ne réintroduise un flag inconnu de la sous-commande. // Prouvé via le sidecar argv du fake enregistreur (jamais le vrai codex). // ===================================================================== - /// Conversation NEUVE : `codex exec --json --skip-git-repo-check - /// --sandbox workspace-write --ask-for-approval never `. On prouve la - /// présence des deux flags d'autonomie ET leur appariement valeur (sandbox= - /// workspace-write, ask-for-approval=never), sans sous-commande `resume`. + /// Conversation NEUVE : argv EXACT `[exec, --json, --skip-git-repo-check, + /// --sandbox, workspace-write, ]`. Pas de sous-commande `resume`, + /// pas de `--ask-for-approval`. #[tokio::test] - async fn codex_new_conversation_command_carries_autonomy_flags() { + async fn codex_new_conversation_command_carries_exact_args() { let (cmd, argv) = make_recording_fake(&[ r#"{"type":"thread.started","thread_id":"cx-new"}"#, r#"{"type":"item.completed","item":{"id":"i0","type":"agent_message","text":"ok"}}"#, @@ -1187,42 +1190,27 @@ mod tests { let recorded = std::fs::read_to_string(&argv).expect("argv"); let args: Vec<&str> = recorded.lines().collect(); - // Conversation neuve : pas de sous-commande resume. - assert!( - !args.contains(&"resume"), - "1er tour NEUF ne doit PAS porter resume, vu: {args:?}" - ); - // Flags d'autonomie (D3), avec leur valeur appariée. - assert!(args.contains(&"--sandbox"), "vu: {args:?}"); - assert!(args.contains(&"workspace-write"), "vu: {args:?}"); - assert!(args.contains(&"--ask-for-approval"), "vu: {args:?}"); - assert!(args.contains(&"never"), "vu: {args:?}"); - // `--sandbox` est immédiatement suivi de `workspace-write`. - let sb = args.iter().position(|a| *a == "--sandbox").unwrap(); assert_eq!( - args.get(sb + 1), - Some(&"workspace-write"), - "--sandbox doit être suivi de workspace-write, vu: {args:?}" + args, + vec![ + "exec", + "--json", + "--skip-git-repo-check", + "--sandbox", + "workspace-write", + "salut", + ], + "argv neuf doit être exact (sans resume, sans --ask-for-approval), vu: {args:?}" ); - // `--ask-for-approval` est immédiatement suivi de `never`. - let af = args.iter().position(|a| *a == "--ask-for-approval").unwrap(); - assert_eq!( - args.get(af + 1), - Some(&"never"), - "--ask-for-approval doit être suivi de never, vu: {args:?}" - ); - // La base + le prompt restent présents. - assert!(args.contains(&"--json") && args.contains(&"--skip-git-repo-check")); - assert!(args.contains(&"salut"), "vu: {args:?}"); let _ = std::fs::remove_file(&cmd); let _ = std::fs::remove_file(&argv); } - /// REPRISE (seed d'id) : `codex exec resume --json --skip-git-repo-check - /// --sandbox workspace-write --ask-for-approval never `. Les flags - /// d'autonomie sont AUSSI présents lors d'une reprise. + /// REPRISE (seed d'id) : argv EXACT `[exec, resume, , --json, + /// --skip-git-repo-check, --sandbox, workspace-write, ]`. Toujours + /// pas de `--ask-for-approval`. #[tokio::test] - async fn codex_resume_command_carries_autonomy_flags() { + async fn codex_resume_command_carries_exact_args() { let (cmd, argv) = make_recording_fake(&[ r#"{"type":"item.completed","item":{"id":"i0","type":"agent_message","text":"ok"}}"#, ]); @@ -1236,20 +1224,20 @@ mod tests { let recorded = std::fs::read_to_string(&argv).expect("argv"); let args: Vec<&str> = recorded.lines().collect(); - // Reprise : sous-commande resume + id. - assert!(args.contains(&"resume"), "vu: {args:?}"); - assert!(args.contains(&"cx-id"), "vu: {args:?}"); - // Flags d'autonomie présents même en reprise. - let sb = args - .iter() - .position(|a| *a == "--sandbox") - .expect("--sandbox présent en reprise"); - assert_eq!(args.get(sb + 1), Some(&"workspace-write"), "vu: {args:?}"); - let af = args - .iter() - .position(|a| *a == "--ask-for-approval") - .expect("--ask-for-approval présent en reprise"); - assert_eq!(args.get(af + 1), Some(&"never"), "vu: {args:?}"); + assert_eq!( + args, + vec![ + "exec", + "resume", + "cx-id", + "--json", + "--skip-git-repo-check", + "--sandbox", + "workspace-write", + "vas-y", + ], + "argv reprise doit être exact (resume en tête, sans --ask-for-approval), vu: {args:?}" + ); let _ = std::fs::remove_file(&cmd); let _ = std::fs::remove_file(&argv); }