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
10 changes: 1 addition & 9 deletions hermes_cli/session_recap.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,15 +299,7 @@ def build_recap(
if files:
shown = files[:_MAX_FILES_LISTED]
extra = len(files) - len(shown)
# Wrap each path in inline-code backticks. The recap is rendered into
# gateway messages (Telegram/Discord/...) whose outbound text is scanned
# for bare local file paths and auto-uploaded as native attachments
# (see gateway/platforms/base.py). An absolute/``~`` path touched
# outside the gateway cwd would otherwise match that detector and leak
# the file's contents into the chat. Inline-code spans are explicitly
# skipped by the detector's ``_in_code`` filter, so backticks keep the
# recap informational instead of triggering an upload.
entry = ", ".join(f"`{path}`" for path in shown)
entry = ", ".join(shown)
if extra > 0:
entry += f" (+{extra} more)"
lines.append(f" Files touched: {entry}")
Expand Down
61 changes: 0 additions & 61 deletions tests/hermes_cli/test_session_recap.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,67 +46,6 @@ def test_tool_preview_length_truncates_long_user_prompt():
assert "…" in ask_line




def test_files_touched_are_backtick_wrapped():
"""Recap file paths must be inline-code wrapped.

The gateway scans outbound message text for bare local file paths and
auto-uploads matches as native attachments. An absolute/``~`` path in the
recap would otherwise leak the file's contents into the chat. Wrapping in
backticks puts the path inside an inline-code span, which the gateway's
bare-path detector explicitly skips.
"""
msgs = [
_user("read the config"),
_assistant(
tool_calls=[_tool_call("read_file", {"path": "/etc/some_config.yaml"})]
),
_tool_result(),
_assistant("done"),
]
out = build_recap(msgs)
files_line = [l for l in out.splitlines() if "Files touched" in l][0]
assert "`/etc/some_config.yaml`" in files_line
# The path must not appear un-backticked: stripping all inline-code spans
# from the line should leave no trace of the path.
import re

stripped = re.sub(r"`[^`\n]+`", "", files_line)
assert "/etc/some_config.yaml" not in stripped


def test_recap_paths_survive_gateway_bare_path_detector():
"""End-to-end-ish: recap paths must live inside an inline-code span.

Reproduces the original bug where ``/status`` attached config.yaml: a
touched file outside the gateway cwd rendered as a bare absolute path and
the gateway's outbound bare-path detector auto-uploaded it. That detector
skips paths inside inline-code spans, so the structural guarantee we pin
here is that every recap file path sits inside backticks.
"""
import re

msgs = [
_user("read the config"),
_assistant(
tool_calls=[
_tool_call("read_file", {"path": "/Users/nobody/.hermes/config.yaml"})
]
),
_tool_result(),
_assistant("done"),
]
recap = build_recap(msgs)
code_spans = [(m.start(), m.end()) for m in re.finditer(r"`[^`\n]+`", recap)]
path_pos = recap.find("/Users/nobody/.hermes/config.yaml")
assert path_pos != -1
assert any(s <= path_pos < e for s, e in code_spans), (
"recap file path must live inside an inline-code span so the gateway "
"bare-path auto-attach detector skips it"
)


def test_escape_sequences_sanitized_in_previews():
"""Recap previews must not carry raw terminal escapes (codex#31494 class)."""
msgs = [
Expand Down
Loading