diff --git a/agent/prompt.py b/agent/prompt.py index b7b290bb..4492f820 100644 --- a/agent/prompt.py +++ b/agent/prompt.py @@ -89,6 +89,7 @@ Format messages using Slack's mrkdwn format, NOT standard Markdown. Key differences: *bold*, _italic_, ~strikethrough~, , bullet lists with "• ", ```code blocks```, > blockquotes. Do NOT use **bold**, [link](url), or other standard Markdown syntax. + To mention/tag a user, use `<@USER_ID>` (e.g. `<@U06KD8BFY95>`). You can find user IDs in the conversation context next to display names (e.g. `@Name(U06KD8BFY95)`). #### `github_comment` Posts a comment to a GitHub issue or pull request. Provide the `issue_number` explicitly. Use this when the task was triggered from GitHub — to reply with updates, answers, or a summary after completing work.""" diff --git a/agent/tools/slack_thread_reply.py b/agent/tools/slack_thread_reply.py index 04b7be6f..9210d14e 100644 --- a/agent/tools/slack_thread_reply.py +++ b/agent/tools/slack_thread_reply.py @@ -3,7 +3,7 @@ from typing import Any from langgraph.config import get_config -from ..utils.slack import post_slack_thread_reply +from ..utils.slack import convert_mentions_to_slack_format, post_slack_thread_reply def slack_thread_reply(message: str) -> dict[str, Any]: @@ -12,7 +12,11 @@ def slack_thread_reply(message: str) -> dict[str, Any]: Format messages using Slack's mrkdwn format, NOT standard Markdown. Key differences: *bold*, _italic_, ~strikethrough~, , bullet lists with "• ", ```code blocks```, > blockquotes. - Do NOT use **bold**, [link](url), or other standard Markdown syntax.""" + Do NOT use **bold**, [link](url), or other standard Markdown syntax. + + To mention/tag a user, use Slack's mention format: <@USER_ID>. + You can find user IDs in the conversation context (e.g. @Name(U06KD8BFY95)). + Example: <@U06KD8BFY95> will tag that user in the message.""" config = get_config() configurable = config.get("configurable", {}) slack_thread = configurable.get("slack_thread", {}) @@ -28,5 +32,6 @@ def slack_thread_reply(message: str) -> dict[str, Any]: if not message.strip(): return {"success": False, "error": "Message cannot be empty"} + message = convert_mentions_to_slack_format(message) success = asyncio.run(post_slack_thread_reply(channel_id, thread_ts, message)) return {"success": success} diff --git a/agent/utils/slack.py b/agent/utils/slack.py index 7da61a05..c54ca290 100644 --- a/agent/utils/slack.py +++ b/agent/utils/slack.py @@ -7,6 +7,7 @@ import hashlib import hmac import logging import os +import re import time from typing import Any @@ -66,6 +67,11 @@ def replace_bot_mention_with_username(text: str, bot_user_id: str, bot_username: return text +def convert_mentions_to_slack_format(text: str) -> str: + """Convert @Name(USER_ID) patterns to Slack's <@USER_ID> mention format.""" + return re.sub(r"@[^()]+\(([A-Z0-9]+)\)", r"<@\1>", text) + + def verify_slack_signature( body: bytes, timestamp: str, diff --git a/tests/test_slack_context.py b/tests/test_slack_context.py index c438d314..698920f1 100644 --- a/tests/test_slack_context.py +++ b/tests/test_slack_context.py @@ -4,6 +4,7 @@ import pytest from agent import webapp from agent.utils.slack import ( + convert_mentions_to_slack_format, format_slack_messages_for_prompt, replace_bot_mention_with_username, select_slack_context_messages, @@ -107,6 +108,28 @@ def test_replace_bot_mention_with_username() -> None: ) +def test_convert_mentions_to_slack_format_basic() -> None: + assert ( + convert_mentions_to_slack_format("Hey @Brace Sproul(U06KD8BFY95), check this") + == "Hey <@U06KD8BFY95>, check this" + ) + + +def test_convert_mentions_to_slack_format_multiple() -> None: + text = "@Alice(U111) and @Bob(U222) please review" + assert convert_mentions_to_slack_format(text) == "<@U111> and <@U222> please review" + + +def test_convert_mentions_to_slack_format_no_match() -> None: + text = "No mentions here, just @plain text" + assert convert_mentions_to_slack_format(text) == text + + +def test_convert_mentions_to_slack_format_preserves_existing_slack_mentions() -> None: + text = "Already tagged <@U06KD8BFY95> correctly" + assert convert_mentions_to_slack_format(text) == text + + def test_format_slack_messages_for_prompt_uses_name_and_id() -> None: formatted = format_slack_messages_for_prompt( [{"ts": "1.0", "text": "hello", "user": "U123"}],