fix(firecracker): quote guest argv tokens so codex's multi-word prompt survives ssh
The interactive agent command is sent to the guest by spreading the remote argv as separate ssh arguments; ssh space-joins everything after the host into one line that the guest login shell re-parses. That only works while every token is a "simple word" — which held for claude (`--append-system-prompt-file <path>`) but not for codex's `read_prompt_file` mode, whose positional is a whole sentence: "Read and follow the instructions in <path>.". The guest shell re-split it on spaces, so codex received `Read` as the prompt and `and`, `follow`, … as extra args — failing with `unrecognized subcommand 'and'` the moment an interactive codex session attached. Pre-quote each remote token with shlex.quote before ssh joins them (the same ssh→guest-shell discipline infra_vm/cp_in already use). Simple words are unchanged, so existing behaviour and the parity/structure tests are untouched; an arg with spaces now survives as a single argument. Regression test round-trips the joined remote command back through shlex.split and asserts codex's prompt comes out as exactly one arg. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
This commit is contained in:
@@ -255,6 +255,30 @@ class TestBottleAgentArgv(unittest.TestCase):
|
||||
argv[idx:],
|
||||
)
|
||||
|
||||
def test_codex_multiword_prompt_survives_ssh_reparse(self):
|
||||
# codex's read_prompt_file mode passes a single positional with
|
||||
# spaces ("Read and follow the instructions in <path>."). ssh
|
||||
# space-joins the remote argv and the guest shell re-splits it, so
|
||||
# the token MUST be quoted or codex sees "and" as a subcommand
|
||||
# (regression). Each remote token is shlex.quote'd; round-tripping
|
||||
# the joined remote command back through shlex.split must recover
|
||||
# the prompt as ONE argument.
|
||||
import shlex
|
||||
|
||||
argv = _bottle(
|
||||
agent_command="codex",
|
||||
agent_prompt_mode="read_prompt_file",
|
||||
agent_provider_template="codex",
|
||||
prompt_path_in_guest="/home/node/.bot-bottle-prompt.txt",
|
||||
).agent_argv([], tty=False)
|
||||
idx = argv.index("--")
|
||||
remote_line = " ".join(argv[idx + 1:]) # what ssh sends to the guest
|
||||
reparsed = shlex.split(remote_line) # what the guest shell sees
|
||||
prompt = "Read and follow the instructions in /home/node/.bot-bottle-prompt.txt."
|
||||
self.assertIn(prompt, reparsed)
|
||||
# codex is the last simple token before the (single) prompt arg.
|
||||
self.assertEqual([*reparsed[reparsed.index("codex"):]], ["codex", prompt])
|
||||
|
||||
def test_workdir_sets_chdir(self):
|
||||
# The agent runs from its workdir via `env --chdir` (ssh-safe;
|
||||
# not a `sh -c 'cd …'` wrapper, which the ssh arg-join mangles).
|
||||
|
||||
Reference in New Issue
Block a user