Skip to content
Open
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
2 changes: 1 addition & 1 deletion cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7798,7 +7798,7 @@ def run_agent():
maybe_auto_title(
self._session_db,
self.session_id,
message,
result.get("original_user_message") or message,
response,
self.conversation_history,
)
Expand Down
3 changes: 2 additions & 1 deletion gateway/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8801,10 +8801,11 @@ def _approval_notify_sync(approval_data: dict) -> None:
try:
from agent.title_generator import maybe_auto_title
all_msgs = result_holder[0].get("messages", []) if result_holder[0] else []
_clean_msg = (result_holder[0] or {}).get("original_user_message") or message
maybe_auto_title(
self._session_db,
effective_session_id,
message,
_clean_msg,
final_response,
all_msgs,
)
Expand Down
1 change: 1 addition & 0 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -10708,6 +10708,7 @@ def _stop_spinner():
"partial": False, # True only when stopped due to invalid tool calls
"interrupted": interrupted,
"response_previewed": getattr(self, "_response_was_previewed", False),
"original_user_message": original_user_message,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

original_user_message is not guaranteed to be clean skill input: the turn setup defines it as persist_user_message only when supplied, otherwise the model-facing user_message (current agent/turn_context.py:304; PR base run_agent.py:7925). Normal CLI and gateway skill paths pass the expanded payload without that override, so this field would still contain the skill body. Use the canonical skill-instruction extractor before title generation instead.

"model": self.model,
"provider": self.provider,
"base_url": self.base_url,
Expand Down
49 changes: 49 additions & 0 deletions tests/test_auto_title_clean_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Tests for auto-title using clean user message instead of skill-bloated input.

When a skill is active, the message passed to run_conversation contains
the full skill content (often 1K+ chars). The title generator truncates
to 500 chars, so it only sees skill boilerplate and generates a wrong
title. The fix passes original_user_message (the clean user input) to
maybe_auto_title instead.
"""

from agent.title_generator import _TITLE_PROMPT


class TestResultIncludesOriginalMessage:

def test_result_dict_has_original_user_message(self):
"""run_conversation result must include original_user_message."""
# Simulate a result dict
result = {
"final_response": "Here are some cat gifs!",
"original_user_message": "find me funny cat gifs",
"messages": [],
}
assert "original_user_message" in result
assert result["original_user_message"] == "find me funny cat gifs"

def test_clean_message_preferred_over_bloated(self):
"""When original_user_message exists, use it over raw message."""
bloated_message = "[SYSTEM: skill content 1000 chars...] find cats"
result = {"original_user_message": "find cats"}

title_input = result.get("original_user_message") or bloated_message
assert title_input == "find cats"
assert len(title_input) < 50 # not the 1000+ char bloated version

def test_fallback_to_message_when_no_original(self):
"""When original_user_message is missing, fall back to raw message."""
message = "hello world"
result = {}

title_input = result.get("original_user_message") or message
assert title_input == "hello world"

def test_title_prompt_fits_clean_input(self):
"""Title prompt truncates to 500 chars — clean input fits easily."""
clean_input = "find me funny cat gifs"
assert len(clean_input) < 500 # will be fully visible to title LLM

bloated = "[SYSTEM: ...skill...] " * 50 + clean_input
assert len(bloated) > 500 # would be truncated, hiding the actual intent
Loading