agent-vm: run shell trailing args via bash -c

Before: `agent-vm shell ls` invoked `bash -O histappend ls`. bash
treats the first non-option positional as a script filename and
PATH-searches for it, lands on `/usr/bin/ls`, hits the ELF magic,
and exits 126 with `/usr/bin/ls: cannot execute binary file`.

Now: when `Agent::Shell` has any trailing args, shell-escape and
join them into a single `-c` command. `agent-vm shell ls -la /tmp`
becomes `bash -O histappend -c "'ls' '-la' '/tmp'"`. No-arg
`agent-vm shell` keeps its interactive-bash path. Other agents
(claude/codex/opencode) are unchanged.

Cargo.lock: workspace agent-vm version bumped 0.1.4 -> 0.1.5 to
match Cargo.toml (stale lockfile entry refreshed on first build).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
agent-vm 2026-05-25 14:08:26 +00:00
parent 3163a5961a
commit 82d97fc4c3
2 changed files with 20 additions and 2 deletions

2
Cargo.lock generated
View file

@ -21,7 +21,7 @@ dependencies = [
[[package]]
name = "agent-vm"
version = "0.1.4"
version = "0.1.5"
dependencies = [
"anyhow",
"base64",

View file

@ -585,7 +585,25 @@ pub async fn launch(agent: Agent, args: Args) -> Result<i32> {
.filter(|d| !args.agent_args.iter().any(|u| u == *d))
.map(|s| s.to_string())
.collect();
inner_args.extend(args.agent_args);
if matches!(agent, Agent::Shell) && !args.agent_args.is_empty() {
// `agent-vm shell foo bar` runs `foo bar` as a command. Without
// `-c`, bash treats the first non-option positional as a script
// filename and PATH-searches for it, so `agent-vm shell ls` lands
// on `/usr/bin/ls` and prints "cannot execute binary file" the
// moment bash hits the ELF magic. Joining the user's args into a
// single `-c` command line (each arg shell-escaped so quoting is
// preserved across the boundary) is the standard fix.
let cmd = args
.agent_args
.iter()
.map(|a| shell_escape(a))
.collect::<Vec<_>>()
.join(" ");
inner_args.push("-c".into());
inner_args.push(cmd);
} else {
inner_args.extend(args.agent_args);
}
// Wrap the agent invocation in a tiny bash prelude that:
//