Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions agent/subdirectory_hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def _add_path_candidate(self, raw_path: str, candidates: Set[Path]):
if parent == p:
break # filesystem root
p = parent
except (OSError, ValueError):
except (OSError, ValueError, RuntimeError):
pass

def _extract_paths_from_command(self, cmd: str, candidates: Set[Path]):
Expand Down Expand Up @@ -241,11 +241,11 @@ def _load_hints_for_directory(self, directory: Path) -> Optional[str]:
rel_path = str(hint_path)
try:
rel_path = str(hint_path.relative_to(self.working_dir))
except ValueError:
except (ValueError, RuntimeError):
try:
rel_path = str(hint_path.relative_to(Path.home()))
rel_path = "~/" + rel_path
except ValueError:
except (ValueError, RuntimeError):
pass # keep absolute
found_hints.append((rel_path, content))
# First match wins per directory (like startup loading)
Expand Down
54 changes: 54 additions & 0 deletions tests/agent/test_subdirectory_hints_tilde.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Regression tests for the home-directory RuntimeError bug.

Without the fix to ``agent/subdirectory_hints.py`` (add ``RuntimeError`` to
the three ``except`` clauses around ``Path.expanduser()`` /
``Path.home()``), the first two tests raise ``RuntimeError`` from inside
the hint walker on POSIX systems.

These tests use pytest's built-in ``tmp_path`` fixture and intentionally
do not depend on the richer ``project`` fixture from
``test_subdirectory_hints.py`` so the file is runnable standalone.
"""

from agent.subdirectory_hints import SubdirectoryHintTracker


class TestSubdirectoryHintTrackerTildeRobustness:
"""Regression: literal ``~`` in tool-call args must not crash the walker."""

def test_tilde_approximately_in_command_does_not_crash(self, tmp_path):
"""LLMs use ``~`` for "approximately" (e.g. ``~500 agencies``).

``pathlib.Path('~500-700').expanduser()`` raises ``RuntimeError`` β€”
the walker must catch this, not propagate it as a tool failure.
"""
tracker = SubdirectoryHintTracker(working_dir=str(tmp_path))
# Heredoc-style terminal command body containing "~500-700"
# used as "approximately 500-700"
cmd = (
"cat > out.md <<EOF\n"
"Segment size signal: ~500-700 agencies in DACH region.\n"
"CVE volume: ~45,000 disclosed in 2025.\n"
"Founder blended rate: ~80/hr.\n"
"EOF"
)
# Must not raise β€” return value can be None / empty
tracker.check_tool_call("terminal", {"command": cmd})

def test_tilde_with_unknown_user_does_not_crash(self, tmp_path):
"""``~unknown_user`` similarly raises RuntimeError on POSIX systems
whose /etc/passwd does not contain that user. Walker must absorb it."""
tracker = SubdirectoryHintTracker(working_dir=str(tmp_path))
cmd = "echo path: ~nonexistent_user_xyzzy_12345/some/file"
# Must not raise
tracker.check_tool_call("terminal", {"command": cmd})

def test_valid_tilde_user_still_works(self, tmp_path):
"""The fix must not regress the legitimate-tilde-user path.

``~`` alone resolves to ``Path.home()`` and should still be
recognised as a candidate path (no exception either way).
"""
tracker = SubdirectoryHintTracker(working_dir=str(tmp_path))
tracker.check_tool_call("terminal", {"command": "ls ~/Documents"})
# No exception, no assertion required