mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
fix(file-ops): keep worktree isolation when restoring preserved cwd (#26211)
The durable _last_known_cwd anchor is keyed by the shared 'default' container, so a non-owning worktree session could inherit the owning session's cwd through it — breaking the wrong-worktree-routing fix (test_file_tools_cwd_resolution:: test_resolution_routes_to_resolving_sessions_worktree). Reorder _authoritative_workspace_root so the session-specific registered cwd override (keyed by raw session id) is checked BEFORE the shared-container _last_known_cwd fallback. A non-owning session now resolves into its own registered worktree; the durable anchor only fills in when there's no session-specific override (the #26211 single-session case). Adds a regression test covering the owner-mirrors-then-other-session-resolves interaction.
This commit is contained in:
parent
b2faeba182
commit
457c8a0a7c
2 changed files with 33 additions and 3 deletions
|
|
@ -394,3 +394,27 @@ def test_unknown_owner_keeps_prior_single_session_behavior(tmp_path, monkeypatch
|
|||
)
|
||||
assert ft._get_live_tracking_cwd("default") == str(ws)
|
||||
assert ft._get_live_tracking_cwd("any-session") == str(ws)
|
||||
|
||||
|
||||
def test_preserved_cwd_does_not_override_non_owning_sessions_worktree(
|
||||
_two_worktree_sessions, monkeypatch
|
||||
):
|
||||
"""#26211 belt-and-suspenders must not break worktree isolation.
|
||||
|
||||
The owner (session B) doing an owned live read mirrors wt_b into the shared
|
||||
_last_known_cwd['default'] registry. Session A — which does NOT own the env
|
||||
but HAS its own registered worktree (wt_a) — must still resolve into wt_a,
|
||||
not inherit B's preserved cwd through the shared-container key. The
|
||||
session-specific registered override must beat the durable shared anchor.
|
||||
"""
|
||||
wt_a, wt_b, _main = _two_worktree_sessions
|
||||
monkeypatch.setattr(ft, "_last_known_cwd", {})
|
||||
|
||||
# Owner B resolves first — this mirrors wt_b into _last_known_cwd['default'].
|
||||
assert ft._resolve_path_for_task("target.py", task_id="sess-b") == (wt_b / "target.py")
|
||||
assert ft._last_known_cwd.get("default") == str(wt_b)
|
||||
|
||||
# A still routes to its own registered worktree despite the shared anchor.
|
||||
resolved_a = ft._resolve_path_for_task("target.py", task_id="sess-a")
|
||||
assert resolved_a == (wt_a / "target.py")
|
||||
assert not str(resolved_a).startswith(str(wt_b))
|
||||
|
|
|
|||
|
|
@ -244,6 +244,15 @@ def _authoritative_workspace_root(task_id: str = "default") -> str | None:
|
|||
live = _get_live_tracking_cwd(task_id)
|
||||
if live:
|
||||
return live
|
||||
# A session-specific registered override (TUI/Desktop/ACP workspace cwd)
|
||||
# is more authoritative than the shared last-known anchor: it is keyed by
|
||||
# the raw session id, so when two worktree sessions share the single
|
||||
# "default" terminal env, a NON-owning session must resolve against its OWN
|
||||
# registered worktree — never the other session's leftover cwd. (Checked
|
||||
# before _last_known_cwd, which is keyed by the shared container id.)
|
||||
registered = _registered_task_cwd_override(task_id)
|
||||
if registered:
|
||||
return registered
|
||||
# When the terminal env was cleaned up mid-conversation, the live cwd is
|
||||
# gone but the directory the agent navigated to is still recorded in the
|
||||
# durable _last_known_cwd registry. Prefer it over the config/process
|
||||
|
|
@ -254,9 +263,6 @@ def _authoritative_workspace_root(task_id: str = "default") -> str | None:
|
|||
preserved = _last_known_cwd_for(task_id)
|
||||
if preserved:
|
||||
return preserved
|
||||
registered = _registered_task_cwd_override(task_id)
|
||||
if registered:
|
||||
return registered
|
||||
return _configured_terminal_cwd()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue