From 72e8e2983ab690b18d1cf3f1a3bbb65508446d13 Mon Sep 17 00:00:00 2001 From: Xue-1997 <102006332+Xue-1997@users.noreply.github.com> Date: Mon, 3 Aug 2026 11:11:30 +0530 Subject: [PATCH] fix(session-search): strip ANSI from recalled messages Recalled session messages can carry raw ANSI escape sequences (e.g. archived terminal output), which then re-enter the model's context. Strip them in _shape_message before content is truncated/returned, reusing tools.ansi_strip.strip_ansi. Re-applied onto current main (the original hunk predates the max_content_len truncation in _shape_message; stripping happens on the raw content before truncation so escape bytes never count against the budget). Extracted from #40276. --- tests/tools/test_session_search.py | 13 +++++++++++++ tools/session_search_tool.py | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/tests/tools/test_session_search.py b/tests/tools/test_session_search.py index 4e50a77b0cc2c..d644ae4b9ae9f 100644 --- a/tests/tools/test_session_search.py +++ b/tests/tools/test_session_search.py @@ -272,6 +272,19 @@ class TestReadShape: assert len(result["messages"]) == 5 assert result["session_meta"]["title"] == "Building the Modpack" + def test_read_strips_ansi_sequences_from_messages(self, db): + db.create_session("s_ansi", source="cli") + db.append_message("s_ansi", role="user", content="plain") + db.append_message( + "s_ansi", role="assistant", content="\u001b[31mred text\u001b[0m and more" + ) + db._conn.commit() + result = json.loads(session_search(session_id="s_ansi", db=db)) + assert result["success"] is True + rendered = [m["content"] for m in result["messages"] if m.get("content")] + assert any(text == "red text and more" for text in rendered) + assert all("\u001b" not in text for text in rendered) + def test_read_truncates_large_session(self, db): db.create_session("s_big", source="cli") for i in range(50): diff --git a/tools/session_search_tool.py b/tools/session_search_tool.py index be063a83e40bb..f25debc0d7075 100644 --- a/tools/session_search_tool.py +++ b/tools/session_search_tool.py @@ -247,6 +247,12 @@ def _shape_message( is added so callers know the payload was bounded. """ raw_content = m.get("content") + if isinstance(raw_content, str) and "\x1b" in raw_content: + # Recalled messages can carry ANSI escape sequences (e.g. archived + # terminal output). Strip them before returning content to the model. + from tools.ansi_strip import strip_ansi + + raw_content = strip_ansi(raw_content) if max_content_len and raw_content and len(raw_content) > max_content_len: content = raw_content[:max_content_len] + "…" truncated = True