fix(cli): refresh TERMINAL_CWD when --in re-homes the session

`--in DIR` only chdir'd. Every cwd consumer (resolve_agent_cwd -> Codex
app-server thread cwd, the terminal tool, context-file discovery) prefers
TERMINAL_CWD over the process cwd, so a value inherited from a parent
Hermes surface, the shell or .env survived the chdir and the session kept
running in the old directory. The local backend was rescued by cli.py's
force-export at import time; docker/ssh backends and the TUI launch path,
which never imports cli.py, were not.

Refresh TERMINAL_CWD to the --in target when it is already set. An unset
variable stays unset so the backends keep deriving from the new process
cwd and no host path is pre-seeded into ssh/container backends.

Fixes #106220
This commit is contained in:
Totoro-qaq
2026-09-09 11:30:16 +08:00
committed by Teknium
parent 060cd7f9bb
commit 5d6d5fb223
2 changed files with 57 additions and 0 deletions
+9
View File
@@ -1443,6 +1443,15 @@ def _apply_in_dir(args) -> None:
except OSError as e:
print(f"Error: cannot enter --in directory {in_dir}: {e}")
sys.exit(1)
# Every cwd consumer (resolve_agent_cwd -> Codex app-server thread cwd, the
# terminal tool, context-file discovery) prefers TERMINAL_CWD over the process
# cwd, so a value inherited from a parent surface, the shell or .env outlives
# this chdir and re-homes the session in the old directory (#106220). Refresh
# it. An unset variable stays unset: the backends then derive from the new
# process cwd (local exports it at cli import, docker mounts it, ssh and
# container backends keep their own remote/sandbox default).
if os.environ.get("TERMINAL_CWD", "").strip():
os.environ["TERMINAL_CWD"] = _target_dir
args.no_restore_cwd = True
@@ -229,3 +229,51 @@ def test_in_dir_expands_user_home(main_mod, launched, monkeypatch, tmp_path):
assert os.getcwd() == str((home / "proj").resolve())
finally:
os.chdir(start)
# ---------------------------------------------------------------------------
# --in must win over an inherited TERMINAL_CWD (#106220)
# ---------------------------------------------------------------------------
def test_in_dir_replaces_inherited_terminal_cwd(main_mod, monkeypatch, tmp_path):
"""A parent Hermes surface, the shell or .env can export TERMINAL_CWD before
the CLI starts. Every cwd consumer prefers that variable over the process
cwd, so a bare chdir left the Codex app-server thread, the terminal tool and
context-file discovery in the inherited directory."""
import os
from pathlib import Path
from agent.runtime_cwd import resolve_agent_cwd
inherited = tmp_path / "inherited"
target = tmp_path / "target"
inherited.mkdir()
target.mkdir()
monkeypatch.chdir(inherited)
monkeypatch.setenv("TERMINAL_CWD", str(inherited))
args = _args(in_dir=str(target))
main_mod._apply_in_dir(args)
assert Path.cwd().resolve() == target.resolve()
assert Path(os.environ["TERMINAL_CWD"]).resolve() == target.resolve()
assert resolve_agent_cwd().resolve() == target.resolve()
assert args.no_restore_cwd is True
def test_in_dir_leaves_unset_terminal_cwd_unset(main_mod, monkeypatch, tmp_path):
"""Without an inherited value the backends already derive from the process
cwd (local exports os.getcwd() at cli import, docker mounts it, the TUI
child inherits the chdir). Pre-seeding a host path here would leak it into
ssh/container backends that must keep their own default."""
import os
target = tmp_path / "target"
target.mkdir()
monkeypatch.chdir(tmp_path)
monkeypatch.delenv("TERMINAL_CWD", raising=False)
main_mod._apply_in_dir(_args(in_dir=str(target)))
assert "TERMINAL_CWD" not in os.environ