Preserve agent cwd through Codex app server

This commit is contained in:
2026-08-23 02:44:06 +00:00
parent a7d61d812e
commit 9afd9e0a99
4 changed files with 21 additions and 7 deletions
+6 -4
View File
@@ -204,14 +204,16 @@ prompt_delivery = "exec" # {kickoff}=prompt file, {session}=session name,
For an `"arg"` backend the flag *templates* are configurable (so you can point at a non-claude
CLI): `resume_flag` (default `--resume '{id}'`), `model_flag` (default `--model '{model}'`),
`remote_control_flag` (default `--remote-control '{session}'`), and `remote_addr_flag` (default
`--remote '{addr}'`). A backend that sets `process_name`
`remote_control_flag` (default `--remote-control '{session}'`), `remote_addr_flag` (default
`--remote '{addr}'`), and `remote_cwd_flag` (default `-C {dir}` whenever `remote_addr` is set).
A backend that sets `process_name`
participates in backend-mismatch healing; one that doesn't (e.g. `demo`) never does.
An optional `preamble` runs first and must succeed before the TUI starts; it supports `{bin}`,
`{session}`, `{model}`, and `{dir}` templates. The Codex backend uses it to idempotently start the
shared Remote Control daemon, then uses `remote_addr = "unix://"` to connect each tmux-hosted Codex
TUI to that daemon. This keeps the app-server as the sole thread-store writer while local and
Remote clients share the session.
TUI to that daemon. The accompanying `-C` is required because the daemon's process directory is
independent of the agent's. This keeps the app-server as the sole thread-store writer while local
and Remote clients share the session in the configured project directory.
For an unattended TUI with a deterministic first-run gate, `startup_prompt_re` opts into one
screen check after `startup_prompt_delay` seconds; on a match, the harness types the explicitly
configured `startup_prompt_response` and `submit_key`. Codex uses this to trust the agent's
+7 -2
View File
@@ -350,10 +350,15 @@ def start_agent(cfg, agent, *, force=False):
parts.append(_render_template(backend.get("remote_control_flag",
"--remote-control '{session}'"), {"session": session}))
if backend.get("remote_addr"):
parts.append(_render_template(backend.get("remote_addr_flag", "--remote '{addr}'"), {
remote_values = {
"addr": backend["remote_addr"], "session": session, "model": model,
"dir": shlex.quote(cwd),
}))
}
parts.append(_render_template(
backend.get("remote_addr_flag", "--remote '{addr}'"), remote_values))
remote_cwd_flag = backend.get("remote_cwd_flag", "-C {dir}")
if remote_cwd_flag:
parts.append(_render_template(remote_cwd_flag, remote_values))
if model:
parts.append(_render_template(backend.get("model_flag", "--model '{model}'"), {"model": model}))
if backend.get("flags"):
+6
View File
@@ -96,6 +96,12 @@ else
fail "Codex probe did not complete (cmd=${cmd}); tail: $(echo "$pane" | grep -vE '^\s*$' | tail -5)"
fi
if echo "$pane" | grep -Fq "$REPO"; then
pass "daemon-created Codex thread uses the requested project directory"
else
fail "Codex thread did not use requested directory $REPO"
fi
if python3 "$REPO/agents.py" --config "$CFG" status \
| grep -E '^\s*probe\b' | grep -q RUNNING; then
pass "agents.py status reports probe RUNNING"
+2 -1
View File
@@ -279,8 +279,9 @@ class TestExampleConfig(unittest.TestCase):
cmd = launched[0]
self.assertTrue(cmd.startswith(
"codex remote-control start --json >/dev/null && "
"codex --remote 'unix://' --model 'gpt-test' "))
"codex --remote 'unix://' -C "))
self.assertEqual(cmd.count("--remote 'unix://'"), 1)
self.assertIn(f"-C {agents.shlex.quote(cfg['agents']['cx']['dir'])}", cmd)
self.assertIn("--model 'gpt-test'", cmd)
self.assertIn(
"--dangerously-bypass-approvals-and-sandbox --dangerously-bypass-hook-trust --no-alt-screen",