diff --git a/agent/utils/slack.py b/agent/utils/slack.py index b231ad37..221ab310 100644 --- a/agent/utils/slack.py +++ b/agent/utils/slack.py @@ -610,6 +610,24 @@ TRACE_REPLY_PHRASES: tuple[str, ...] = ( "Running to the roar!", ) +TRACE_REPLY_TIPS: tuple[str, ...] = ( + "You can message me in this thread while I'm running — I'll pick up your follow-up before my next step.", + "Kick off another task in parallel — each one runs in its own isolated sandbox, no queuing.", + "Add `repo:owner/name` to your message to point me at a different repo for this task.", + "Drop an `AGENTS.md` at your repo root and I'll read it on every run — it's the easiest way to teach me your conventions.", + "I'll open a draft PR automatically when I'm done and link it back here.", + "Tag me on a PR comment of an open-swe PR to have me address review feedback on the same branch.", + "I can spawn subagents for independent subtasks — useful for parallel research or fan-out work.", + "Click `View trace` above to watch every tool call and model response live in LangSmith.", +) + + +def _format_trace_reply(message: str, trace_url: str | None) -> str: + """Format the initial trace reply with a randomly selected tip.""" + tip = random.choice(TRACE_REPLY_TIPS) + head = f"{message} <{trace_url}|View trace>" if trace_url else message + return f"{head}\n_Tip: {tip}_" + async def post_slack_trace_reply( channel_id: str, thread_ts: str, thread_id: str, message: str | None = None @@ -618,7 +636,4 @@ async def post_slack_trace_reply( if message is None: message = random.choice(TRACE_REPLY_PHRASES) trace_url = get_langsmith_trace_url(thread_id) - if trace_url: - await post_slack_thread_reply(channel_id, thread_ts, f"{message} <{trace_url}|View trace>") - else: - await post_slack_thread_reply(channel_id, thread_ts, message) + await post_slack_thread_reply(channel_id, thread_ts, _format_trace_reply(message, trace_url)) diff --git a/tests/test_slack_context.py b/tests/test_slack_context.py index c0322b93..5667abd7 100644 --- a/tests/test_slack_context.py +++ b/tests/test_slack_context.py @@ -6,6 +6,7 @@ from agent import webapp from agent.utils import slack as slack_utils from agent.utils.slack import ( TRACE_REPLY_PHRASES, + TRACE_REPLY_TIPS, convert_mentions_to_slack_format, format_slack_messages_for_prompt, looks_like_slack_pr_review_command, @@ -235,7 +236,10 @@ def test_post_slack_trace_reply_picks_random_phrase_when_no_message( asyncio.run(post_slack_trace_reply("C123", "1.0", "thread-id")) assert len(posted) == 1 - assert posted[0] in TRACE_REPLY_PHRASES + head, _, tip_line = posted[0].partition("\n") + assert head in TRACE_REPLY_PHRASES + assert tip_line.startswith("_Tip: ") and tip_line.endswith("_") + assert any(tip in tip_line for tip in TRACE_REPLY_TIPS) def test_post_slack_trace_reply_uses_explicit_message_when_provided( @@ -251,7 +255,11 @@ def test_post_slack_trace_reply_uses_explicit_message_when_provided( asyncio.run(post_slack_trace_reply("C123", "1.0", "thread-id", message="Taking a look...")) - assert posted == ["Taking a look..."] + assert len(posted) == 1 + head, _, tip_line = posted[0].partition("\n") + assert head == "Taking a look..." + assert tip_line.startswith("_Tip: ") and tip_line.endswith("_") + assert any(tip in tip_line for tip in TRACE_REPLY_TIPS) def test_select_slack_context_messages_detects_username_mention() -> None: