Skip to content
Merged
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
10 changes: 5 additions & 5 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5097,9 +5097,9 @@ def _init_agent(self, *, model_override: str = None, runtime_override: dict = No
resolved_id = self.session_id
if resolved_id and resolved_id != self.session_id:
ChatConsole().print(
f"[{_DIM}]Session {_escape(self.session_id)} was compressed into "
f"[dim]Session {_escape(self.session_id)} was compressed into "
f"{_escape(resolved_id)}; resuming the descendant with your "
f"transcript.[/]"
f"transcript.[/dim]"
)
self.session_id = resolved_id
resolved_meta = self._session_db.get_session(self.session_id)
Expand Down Expand Up @@ -5378,7 +5378,7 @@ def _restore_session_cwd(self, session_meta: dict, *, quiet: bool = False) -> No
if quiet:
print(msg, file=sys.stderr)
else:
self._console_print(f"[{_DIM}]{_escape(msg)}[/]")
self._console_print(f"[dim]{_escape(msg)}[/dim]")
return

try:
Expand All @@ -5388,7 +5388,7 @@ def _restore_session_cwd(self, session_meta: dict, *, quiet: bool = False) -> No
if quiet:
print(msg, file=sys.stderr)
else:
self._console_print(f"[{_DIM}]{_escape(msg)}[/]")
self._console_print(f"[dim]{_escape(msg)}[/dim]")
return

# Retarget the terminal/code-exec tools to match the process cwd.
Expand All @@ -5398,7 +5398,7 @@ def _restore_session_cwd(self, session_meta: dict, *, quiet: bool = False) -> No
if quiet:
print(msg, file=sys.stderr)
else:
self._console_print(f"[{_DIM}]{_escape(msg)}[/]")
self._console_print(f"[dim]{_escape(msg)}[/dim]")

def _preload_resumed_session(self) -> bool:
"""Load a resumed session's history from the DB early (before first chat).
Expand Down
66 changes: 66 additions & 0 deletions tests/cli/test_cli_resume_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,69 @@ def test_pending_disarmed_by_other_command(self):

# A non-resume command disarms the one-shot prompt (#34584).
assert cli_obj._pending_resume_sessions is None


class TestRestoreSessionCwdMarkup:
"""Regression: _restore_session_cwd must not crash with Rich MarkupError.

Lines that used ``[{_DIM}]`` inside Rich markup triggered
``rich.errors.MarkupError: closing tag [/] at position N has nothing to
close`` because ``_DIM`` is an ANSI escape (``\\x1b[2;3m``), not a valid
Rich tag. The fix replaces ``[{_DIM}]`` with Rich's native ``[dim]`` tag.
See: https://github.com/NousResearch/hermes-agent/issues/39469
"""

def test_missing_dir_does_not_raise_markup_error(self):
"""Session cwd gone β†’ dim warning, no MarkupError."""
cli_obj = _make_cli()
console = MagicMock()
cli_obj._output_console = MagicMock(return_value=console)

# Use a path that definitely does not exist.
cli_obj._restore_session_cwd({"cwd": "/nonexistent/path/to/nowhere"})

# Should have printed a warning via console.print, not crashed.
assert console.print.called
printed = str(console.print.call_args)
assert "Working directory is gone" in printed or "gone" in printed.lower()

def test_chdir_failure_does_not_raise_markup_error(self, tmp_path):
"""os.chdir fails β†’ dim warning, no MarkupError."""
import os
cli_obj = _make_cli()
console = MagicMock()
cli_obj._output_console = MagicMock(return_value=console)

# Create a directory, then make it unreadable (simulate chdir failure).
target = tmp_path / "locked"
target.mkdir()

# Patch os.chdir to raise OSError for our target path.
original_chdir = os.chdir
def fake_chdir(path):
if str(path) == str(target):
raise OSError("Permission denied")
return original_chdir(path)

with patch("os.chdir", side_effect=fake_chdir):
cli_obj._restore_session_cwd({"cwd": str(target)})

assert console.print.called
printed = str(console.print.call_args)
assert "Could not enter" in printed or "permission" in printed.lower()

def test_success_path_does_not_raise_markup_error(self, tmp_path):
"""Successful cwd switch β†’ dim info, no MarkupError."""
import os
cli_obj = _make_cli()
console = MagicMock()
cli_obj._output_console = MagicMock(return_value=console)

original_cwd = os.getcwd()
try:
cli_obj._restore_session_cwd({"cwd": str(tmp_path)})
assert console.print.called
printed = str(console.print.call_args)
assert "Working directory" in printed or "working" in printed.lower()
finally:
os.chdir(original_cwd)
Loading