# NousResearch/hermes-agent · Working directory handling fixes across session types

## Open now

- Nothing open on GitHub.

A bug and several fixes addressing how the configured or current working directory is tracked and applied across app-server sessions, terminal sessions, and desktop workspaces. Changes include per-session cwd records, seeding sessions with the correct cwd, and ensuring configured directories take precedence over stale remembered values.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #50451. Verify it addresses your case.
- **Recommended:** read PR #50451 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `resolved` · deciding PR(s): `pr-50451`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Closed / duplicate issues

- #38775: \[Bug\]: Codex app-server sessions ignore configured/session cwd and start sandbox in Hermes home

## Prior & competing PRs

- **#50451: fix(codex): seed app-server sessions with configured cwd**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `acp_adapter/session.py`, `agent/codex_runtime.py`, `tests/acp/test_session.py`, `tests/run_agent/test_codex_app_server_integration.py`
  - fix hunks:
  - `acp_adapter/session.py`

    ```diff
    @@ -617,6 +617,10 @@ def _make_agent(
     
             _register_task_cwd(session_id, cwd)
             agent = AIAgent(**kwargs)
    +        # Codex app-server sessions are spawned lazily on the first turn. Stamp
    +        # the ACP workspace onto the agent so the Codex runtime starts from the
    +        # editor/session cwd instead of the Hermes daemon's process cwd.
    +        agent.session_cwd = cwd
             # ACP stdio transport requires stdout to remain protocol-only JSON-RPC.
             # Route any incidental human-readable agent output to stderr instead.
             agent._print_fn = _acp_stderr_print
    ```
  - `agent/codex_runtime.py`

    ```diff
    @@ -250,7 +250,9 @@ def run_codex_app_server_turn(
         # Spawned on first turn, reused across turns, closed at AIAgent
         # shutdown (see _cleanup hook).
         if not hasattr(agent, "_codex_session") or agent._codex_session is None:
    -        cwd = getattr(agent, "session_cwd", None) or os.getcwd()
    +        from agent.runtime_cwd import resolve_agent_cwd
    +
    +        cwd = getattr(agent, "session_cwd", None) or str(resolve_agent_cwd())
             # Approval callback: defer to Hermes' standard prompt flow if a
             # CLI thread has installed one. Gateway / cron contexts get the
             # codex-side fail-closed default.
    ```
  - `tests/acp/test_session.py`

    ```diff
    @@ -77,6 +77,50 @@ def test_get_session(self, manager):
         def test_get_nonexistent_session_returns_none(self, manager):
             assert manager.get_session("does-not-exist") is None
     
    +    def test_make_agent_stamps_session_cwd_for_codex_runtime(self, monkeypatch):
    +        class FakeAgent:
    +            model = "fake-model"
    +
    +            def __init__(self, **kwargs):
    +                self.kwargs = kwargs
    +
    +        monkeypatch.setattr("run_agent.AIAgent", FakeAgent)
    +        monkeypatch.setattr(
    +            "acp_adapter.session.load_config",
    +            lambda: {
    +                "model": {
    +                    "default": "fake-model",
    +                    "provider": "fake-provider",
    +                },
    +                "mcp_servers": {},
    +            },
    +            raising=False,
    +        )
    +        monkeypatch.setattr(
    +            "hermes_cli.config.load_config",
    +            lambda: {
    +                "model": {
    +                    "default": "fake-model",
    +                    "provider": "fake-provider",
    +                },
    +                "mcp_servers": {},
    +            },
    +        )
    +        monkeypatch.setattr(
    +            "hermes_cli.runtime_provider.resolve_runtime_provider",
    +            lambda requested=None: {
    +                "provider": requested,
    +                "api_mode": "codex_app_server",
    +                "base_url": "https://example.invalid",
    +                "api_key": "[redacted:secret-assignment]",
    +            },
    +        )
    +        monkeypatch.setattr("acp_adapter.session._register_task_cwd", lambda task_id, cwd: None)
    +
    +        state = SessionManager(db=None).create_session(cwd="/tmp/project")
    +
    +        assert state.agent.session_cwd == "/tmp/project"
    +
     
     
     
    ```
  - `tests/run_agent/test_codex_app_server_integration.py`

    ```diff
    @@ -293,6 +293,39 @@ def test_chat_completions_loop_is_not_entered(self, fake_session):
                 agent.run_conversation("hi")
             assert not client_mock.chat.completions.create.called
     
    +    def test_gateway_terminal_cwd_seeds_codex_thread_cwd(self, monkeypatch, tmp_path):
    +        """Gateway sessions set TERMINAL_CWD without stamping agent.session_cwd.
    +        Codex app-server must still start in that configured workspace instead
    +        of falling back to the Hermes daemon process cwd."""
    +        from agent.transports.codex_app_server_session import (
    +            CodexAppServerSession, TurnResult,
    +        )
    +
    +        captured: dict[str, str] = {}
    +
    +        def fake_init(self, **kwargs):
    +            captured["cwd"] = kwargs["cwd"]
    +            self._thread_id = "thread-stub-1"
    +
    +        def fake_run_turn(self, user_input: str, **kwargs):
    +            return TurnResult(
    +                final_text="ok",
    +                projected_messages=[{"role": "assistant", "content": "ok"}],
    +                turn_id="turn-stub-1",
    +                thread_id="thread-stub-1",
    +            )
    +
    +        monkeypatch.setenv("TERMINAL_CWD", str(tmp_path))
    +        monkeypatch.setattr(CodexAppServerSession, "__init__", fake_init)
    +        monkeypatch.setattr(CodexAppServerSession, "run_turn", fake_run_turn)
    +
    +        agent = _make_codex_agent()
    +        assert not hasattr(agent, "session_cwd")
    +        with patch.object(agent, "_spawn_background_review", return_value=None):
    +            agent.run_conversation("hi")
    +
    +        assert captured["cwd"] == str(tmp_path)
    +
     
     class TestReviewForkApiModeDowngrade:
         """When the parent agent runs on codex_app_server, the background
    ```
- **#38778: fix(codex): seed app-server sessions with configured cwd**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `c11ae8261`, `#50451`

## Fix status

- Merged to main in PR #50451 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-06-21`.
- Subsystem: `acp`
