open-swe/agent/tools/slack_read_thread_messages.py
Johannes du Plessis 98b824bd54
feat: add size caps for PR diff, fetch_url, Slack threads, pagination, message queue [closes OPE-51] (#1567)
* feat: add size caps for PR diff, fetch_url, Slack threads, pagination, message queue

Per-source byte/token caps with explicit truncation markers to prevent
unbounded payloads from blowing up LLM context/memory.

- reviewer_diff.py: cap PR diff at 200K chars with head+tail truncation
- fetch_url.py: cap markdownify output at 100K chars
- slack.py: cap thread message fetch at 500 messages
- github_comments.py: cap _fetch_paginated at 50 pages
- thread_ops.py: cap queued messages at 100 (drop oldest)

Closes OPE-51

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

* fix: compute diff line set from full diff, keep most recent Slack messages

Address PR review comments:

1. Truncated diffs rejected valid findings: fetch_pr_diff now returns the
   full diff; truncate_diff is called separately in reviewer.py so the
   line set used for add_finding/publish_review validation is computed
   from the complete diff, not the truncated prompt text.

2. Slack cap dropped recent thread context: fetch_slack_thread_messages
   now keeps the most recent SLACK_THREAD_MAX_MESSAGES messages (was
   keeping the oldest). The tool surfaces a truncation marker in the
   formatted output so the LLM knows the thread was truncated.

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-06-17 15:40:59 -07:00

63 lines
2.2 KiB
Python

import asyncio
from typing import Any
from ..utils.slack import (
SLACK_THREAD_MAX_MESSAGES,
fetch_slack_thread_messages,
format_slack_messages_for_prompt,
get_slack_user_names,
)
async def _fetch_and_format(channel_id: str, message_ts: str) -> dict[str, Any]:
"""Fetch thread messages and resolve author names."""
messages = await fetch_slack_thread_messages(channel_id, message_ts)
if not messages:
return {"success": False, "messages": []}
user_ids = [
msg.get("user") for msg in messages if isinstance(msg.get("user"), str) and msg.get("user")
]
user_names = await get_slack_user_names(user_ids) if user_ids else {}
truncated = len(messages) >= SLACK_THREAD_MAX_MESSAGES
formatted = format_slack_messages_for_prompt(messages, user_names)
if truncated:
formatted = (
f"[thread truncated — showing most recent {len(messages)} messages]\n{formatted}"
)
return {
"success": True,
"formatted": formatted,
"count": len(messages),
"truncated": truncated,
}
def slack_read_thread_messages(channel_id: str, message_ts: str) -> dict[str, Any]:
"""Read messages from a Slack thread.
Use this tool to read messages from a Slack channel or thread.
Provide the channel_id and message_ts (thread timestamp) to fetch all
messages in that thread.
If you encounter a Slack message URL like
https://workspace.slack.com/archives/C0AME1J0/p1776281321762829
you can extract the channel_id (C0AME1J0) and convert the timestamp
by inserting a dot 6 digits from the end (1776281321.762829).
Returns formatted thread messages with author names."""
if not channel_id or not channel_id.strip():
return {"success": False, "error": "channel_id is required"}
if not message_ts or not message_ts.strip():
return {"success": False, "error": "message_ts is required"}
result = asyncio.run(_fetch_and_format(channel_id.strip(), message_ts.strip()))
if not result.get("success"):
return {
"success": False,
"error": "Could not fetch thread messages. The bot may not have access to "
"that channel, or the message may have been deleted.",
}
return result