diff --git a/nanobot/utils/helpers.py b/nanobot/utils/helpers.py
index 3ce15ad6..ae008bec 100644
--- a/nanobot/utils/helpers.py
+++ b/nanobot/utils/helpers.py
@@ -63,8 +63,8 @@ def strip_think(text: str) -> str:
# Stream chunks may end in the middle of a control tag. Strip only known
# control-token prefixes at the very end.
partial_control_tag = (
- r"?(?:t|th|thi|thin|think|tho|thou|thoug|though|thought)"
- r"|<\|?(?:c|ch|cha|chan|chann|channe|channel|channel\|?)"
+ r"?(?:t|th|thi|thin|think|tho|thou|thoug|though|thought)>?"
+ r"|<\|?(?:c|ch|cha|chan|chann|channe|channel)(?:\|?>?)?"
)
text = re.sub(rf"(?:{partial_control_tag})$", "", text)
text = re.sub(r"^\s*<\|?$", "", text)
diff --git a/tests/agent/test_runner.py b/tests/agent/test_runner.py
index 79af0a67..10d25348 100644
--- a/tests/agent/test_runner.py
+++ b/tests/agent/test_runner.py
@@ -993,6 +993,27 @@ async def test_loop_stream_filter_hides_partial_trailing_think_prefix(tmp_path):
assert deltas == ["Hello", " World"]
+@pytest.mark.asyncio
+async def test_loop_stream_filter_hides_complete_trailing_think_tag(tmp_path):
+ loop = _make_loop(tmp_path)
+ deltas: list[str] = []
+
+ async def chat_stream_with_retry(*, on_content_delta, **kwargs):
+ await on_content_delta("Hello ")
+ await on_content_delta("hiddenWorld")
+ return LLMResponse(content="Hello hiddenWorld", tool_calls=[], usage={})
+
+ loop.provider.chat_stream_with_retry = chat_stream_with_retry
+
+ async def on_stream(delta: str) -> None:
+ deltas.append(delta)
+
+ final_content, _, _, _, _ = await loop._run_agent_loop([], on_stream=on_stream)
+
+ assert final_content == "Hello World"
+ assert deltas == ["Hello", " World"]
+
+
@pytest.mark.asyncio
async def test_loop_retries_think_only_final_response(tmp_path):
loop = _make_loop(tmp_path)
diff --git a/tests/utils/test_strip_think.py b/tests/utils/test_strip_think.py
index 7adbe7bc..5db93e65 100644
--- a/tests/utils/test_strip_think.py
+++ b/tests/utils/test_strip_think.py
@@ -105,10 +105,12 @@ class TestStripThinkMalformedLeaks:
def test_partial_trailing_think_tag_after_visible_text(self):
assert strip_think("喷泉策略说明 ") == "answer"
def test_partial_trailing_channel_marker_after_visible_text(self):
assert strip_think("喷泉策略说明 <|chan") == "喷泉策略说明"
assert strip_think("answer ") == "answer"
class TestStripThinkConservativePreserve: