From 8c2a09a0f178001eaa623ba9b0207c3f3a0924d4 Mon Sep 17 00:00:00 2001 From: Jacob Stephens Date: Fri, 11 Sep 2026 17:03:45 +0000 Subject: [PATCH] Omit -a on-request from shipped codex harness to permit --yolo Codex CLI treats --ask-for-approval (-a ) and --dangerously-bypass-approvals-and-sandbox (--yolo) as mutually exclusive clap arguments. Because vaulted-agent forwards operator flags by appending them to the harness command line, having -a on-request baked into etc/harnesses.d/codex.conf caused 'va codex --yolo' to fail at clap validation. Codex already defaults to OnRequest when no approval flag is given, so omitting -a on-request preserves normal behavior while allowing --yolo (or custom -a overrides) to pass cleanly. Also: - Update README pick example row for codex - Add regression test in tests/cli_launch.rs - Add troubleshooting entry in AGENTS.md for permission denied on project configs in operator homes with workdir=caller --- AGENTS.md | 1 + README.md | 2 +- etc/harnesses.d/codex.conf | 2 +- tests/cli_launch.rs | 54 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 773ddd9..7574811 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -229,6 +229,7 @@ Interpret carefully: | `op.env: missing` | File really absent (or not a file) | | `op.env: unreadable (… as user)` | Present but EACCES - often need `service_user` or group/ACL, **not** a paste of the vault SA token | | `cannot enter /home/…` | `workdir=caller` + service account cannot traverse (often `setfacl -m u::x /home/`) | +| `Failed to read project config … Permission denied` | `workdir=caller` in an operator home where agent config (e.g. `~/.codex/config.toml`) is mode 0600 — grant service account read: `setfacl -m u::r ~/.codex/config.toml` && `setfacl -d -m u::r ~/.codex` | | `op cannot parse N reference(s)` | Only **malformed `op://`** lines - plain literals (region, URL) are fine | | `could not resolve` / item named on validate or launch | Well-formed ref, vault item missing or renamed - fix the refs file or vault. Launch lists the variables implicated and suggests `secrets validate` | | `Dangling refs in ` on refresh | Mappings matching nothing the token can see - on 1Password, a missing item or field. Reported every run; exit stays 0. `--prune` removes them | diff --git a/README.md b/README.md index 319044c..7534247 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,7 @@ $ vaulted-agent pick 1) claude claude --permission-mode auto full.env.tpl 2) claude-ro claude --permission-mode auto readonly.env.tpl - 3) codex codex -s danger-full-access -a on-r... limited.env.tpl + 3) codex codex -s danger-full-access limited.env.tpl 4) grok grok readonly.env.tpl harness [1-4, q to quit]: 2 diff --git a/etc/harnesses.d/codex.conf b/etc/harnesses.d/codex.conf index a3b1123..77fe247 100644 --- a/etc/harnesses.d/codex.conf +++ b/etc/harnesses.d/codex.conf @@ -7,4 +7,4 @@ bin = $HOME/.local/bin manifest = limited.env.tpl labels = no workdir = caller -command = codex -s danger-full-access -a on-request +command = codex -s danger-full-access diff --git a/tests/cli_launch.rs b/tests/cli_launch.rs index 951ee0c..634a624 100644 --- a/tests/cli_launch.rs +++ b/tests/cli_launch.rs @@ -165,6 +165,60 @@ fn shipped_agy_harness_injects_manifest_and_preserves_native_args() { assert!(rec.contains("ENV APP_TOKEN"), "{rec}"); } +#[test] +fn shipped_codex_harness_preserves_extra_args_without_approval_conflict() { + let seam = CliSeam::new(); + let home = seam.root.join("home"); + let home_bin = home.join(".local/bin"); + fs::create_dir_all(&home_bin).unwrap(); + fs::rename(seam.install_stub_agent("codex"), home_bin.join("codex")).unwrap(); + seam.install_fake_op(); + fs::write( + seam.config_dir.join("defaults.conf"), + "auth_mode = file\ndefault_backend = onepassword\n", + ) + .unwrap(); + fs::write( + seam.config_dir.join("op.env"), + "OP_SERVICE_ACCOUNT_TOKEN=dummy\n", + ) + .unwrap(); + fs::write( + seam.config_dir.join("manifests/limited.env.tpl"), + "APP_TOKEN=from-manifest\n", + ) + .unwrap(); + fs::copy( + format!("{}/etc/harnesses.d/codex.conf", env!("CARGO_MANIFEST_DIR")), + seam.config_dir.join("harnesses.d/codex.conf"), + ) + .unwrap(); + + let out = seam + .vaulted_agent() + .env("HOME", home) + .env("VAULTED_AGENT_HANDOFF", "spawn") + .args(["codex", "--yolo"]) + .output() + .expect("launch"); + assert!( + out.status.success(), + "stderr={} stdout={}", + String::from_utf8_lossy(&out.stderr), + String::from_utf8_lossy(&out.stdout) + ); + let rec = seam.read_stub_record("codex"); + assert!( + rec.contains("ARGV: -s danger-full-access --yolo"), + "shipped codex harness argv mismatch: {rec}" + ); + assert!( + !rec.contains("-a on-request"), + "must not bake in -a on-request: {rec}" + ); + assert!(rec.contains("ENV APP_TOKEN"), "{rec}"); +} + #[test] fn manifest_override_swaps_which_secrets_reach_the_agent() { // `va -m other.env claude` — the harness still decides the command, the