From f4b9b525fbe59ef6d91c6e33ea6e7c0a03ec4aec Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Mon, 23 Feb 2026 16:11:23 -0800 Subject: [PATCH 1/4] fix: collect only comments since last agent response for follow-up triggers --- apps/agent/agent/webapp.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/apps/agent/agent/webapp.py b/apps/agent/agent/webapp.py index 93296acb..a872a9f7 100644 --- a/apps/agent/agent/webapp.py +++ b/apps/agent/agent/webapp.py @@ -586,29 +586,30 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 ) if comments: - last_bot_comment_idx = -1 - for i, comment in enumerate(comments): + # Sort newest-first so we can collect forward until we hit the last agent response + sorted_comments = sorted( + comments, + key=lambda c: c.get("createdAt", ""), + reverse=True, + ) + + # Collect user comments since the last agent response. + # Iterate newest-first and stop as soon as we see a bot message. + recent_user_comments = [] + for comment in sorted_comments: body = comment.get("body", "") if any(body.startswith(prefix) for prefix in bot_message_prefixes): - last_bot_comment_idx = i + break # Everything after this is from before the last agent response + recent_user_comments.append(comment) - relevant_comments = [] - for i, comment in enumerate(comments): - if i <= last_bot_comment_idx: - continue - body = comment.get("body", "") - if "@openswe" in body.lower(): - relevant_comments.append(comment) - relevant_comments.extend(comments[i + 1 :]) - break + # Reverse to restore chronological (oldest-first) order for the prompt + recent_user_comments.reverse() - if relevant_comments: + if recent_user_comments: comments_text = "\n\n## Comments:\n" - for comment in relevant_comments: + for comment in recent_user_comments: author = comment.get("user", {}).get("name", "Unknown") body = comment.get("body", "") - if any(body.startswith(prefix) for prefix in bot_message_prefixes): - continue comments_text += f"\n**{author}:** {body}\n" prompt = ( From 0e9f830f646ee97bff578771e127c3781886b92c Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Tue, 24 Feb 2026 10:44:17 -0800 Subject: [PATCH 2/4] refactor: extract recent comment logic and add tests --- apps/agent/agent/utils/comments.py | 40 ++++++++++++++++++++++++ apps/agent/agent/webapp.py | 21 ++----------- apps/agent/tests/test_recent_comments.py | 27 ++++++++++++++++ 3 files changed, 69 insertions(+), 19 deletions(-) create mode 100644 apps/agent/agent/utils/comments.py create mode 100644 apps/agent/tests/test_recent_comments.py diff --git a/apps/agent/agent/utils/comments.py b/apps/agent/agent/utils/comments.py new file mode 100644 index 00000000..0c20e8db --- /dev/null +++ b/apps/agent/agent/utils/comments.py @@ -0,0 +1,40 @@ +"""Helpers for Linear comment processing.""" + +from __future__ import annotations + +from typing import Any, Sequence + + +def get_recent_comments( + comments: Sequence[dict[str, Any]], bot_message_prefixes: Sequence[str] +) -> list[dict[str, Any]] | None: + """Return user comments since the last agent response, or None if none. + + Args: + comments: Linear issue comments. + bot_message_prefixes: Prefixes that identify agent/bot responses. + + Returns: + Chronological list of comments since the last agent response, or None. + """ + if not comments: + return None + + sorted_comments = sorted( + comments, + key=lambda comment: comment.get("createdAt", ""), + reverse=True, + ) + + recent_user_comments: list[dict[str, Any]] = [] + for comment in sorted_comments: + body = comment.get("body", "") + if any(body.startswith(prefix) for prefix in bot_message_prefixes): + break # Everything after this is from before the last agent response + recent_user_comments.append(comment) + + if not recent_user_comments: + return None + + recent_user_comments.reverse() + return recent_user_comments diff --git a/apps/agent/agent/webapp.py b/apps/agent/agent/webapp.py index a872a9f7..15a51571 100644 --- a/apps/agent/agent/webapp.py +++ b/apps/agent/agent/webapp.py @@ -15,6 +15,7 @@ from langgraph_sdk import get_client # Local import for encryption from .encryption import encrypt_token +from .utils.comments import get_recent_comments logger = logging.getLogger(__name__) @@ -586,25 +587,7 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 ) if comments: - # Sort newest-first so we can collect forward until we hit the last agent response - sorted_comments = sorted( - comments, - key=lambda c: c.get("createdAt", ""), - reverse=True, - ) - - # Collect user comments since the last agent response. - # Iterate newest-first and stop as soon as we see a bot message. - recent_user_comments = [] - for comment in sorted_comments: - body = comment.get("body", "") - if any(body.startswith(prefix) for prefix in bot_message_prefixes): - break # Everything after this is from before the last agent response - recent_user_comments.append(comment) - - # Reverse to restore chronological (oldest-first) order for the prompt - recent_user_comments.reverse() - + recent_user_comments = get_recent_comments(comments, bot_message_prefixes) if recent_user_comments: comments_text = "\n\n## Comments:\n" for comment in recent_user_comments: diff --git a/apps/agent/tests/test_recent_comments.py b/apps/agent/tests/test_recent_comments.py new file mode 100644 index 00000000..750bbad3 --- /dev/null +++ b/apps/agent/tests/test_recent_comments.py @@ -0,0 +1,27 @@ +from agent.utils.comments import get_recent_comments + + +def test_get_recent_comments_returns_none_for_empty() -> None: + assert get_recent_comments([], ("🤖 **Agent Response**",)) is None + + +def test_get_recent_comments_returns_none_when_newest_is_bot_message() -> None: + comments = [ + {"body": "🤖 **Agent Response** latest", "createdAt": "2024-01-03T00:00:00Z"}, + {"body": "user comment", "createdAt": "2024-01-02T00:00:00Z"}, + ] + + assert get_recent_comments(comments, ("🤖 **Agent Response**",)) is None + + +def test_get_recent_comments_collects_since_last_bot_message() -> None: + comments = [ + {"body": "first user", "createdAt": "2024-01-01T00:00:00Z"}, + {"body": "🤖 **Agent Response** done", "createdAt": "2024-01-02T00:00:00Z"}, + {"body": "follow up 1", "createdAt": "2024-01-03T00:00:00Z"}, + {"body": "follow up 2", "createdAt": "2024-01-04T00:00:00Z"}, + ] + + result = get_recent_comments(comments, ("🤖 **Agent Response**",)) + assert result is not None + assert [comment["body"] for comment in result] == ["follow up 1", "follow up 2"] From 98b886116c955f5a64f0b04a5e57c2d7885ad287 Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Tue, 24 Feb 2026 10:58:20 -0800 Subject: [PATCH 3/4] fix: apply lint/format updates --- apps/agent/agent/utils/comments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/agent/agent/utils/comments.py b/apps/agent/agent/utils/comments.py index 0c20e8db..7f08d0e7 100644 --- a/apps/agent/agent/utils/comments.py +++ b/apps/agent/agent/utils/comments.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, Sequence +from collections.abc import Sequence +from typing import Any def get_recent_comments( From 19cdb32f05391d99d2d5284cc40008236b4fd978 Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Tue, 24 Feb 2026 17:51:29 -0800 Subject: [PATCH 4/4] fix: use get_recent_comments() in multimodal fallback and fix NameError --- apps/agent/agent/webapp.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/apps/agent/agent/webapp.py b/apps/agent/agent/webapp.py index 59f07f23..4b5d828c 100644 --- a/apps/agent/agent/webapp.py +++ b/apps/agent/agent/webapp.py @@ -603,15 +603,11 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 comment_ids: set[str] = set() comment_id_to_index: dict[str, int] = {} if comments: - last_bot_comment_idx = -1 for i, comment in enumerate(comments): comment_id = comment.get("id", "") if comment_id: comment_ids.add(comment_id) comment_id_to_index[comment_id] = i - body = comment.get("body", "") - if any(body.startswith(prefix) for prefix in bot_message_prefixes): - last_bot_comment_idx = i relevant_comments = [] trigger_index = None @@ -624,18 +620,11 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 trigger_index, ) else: - for i, comment in enumerate(comments): - if i <= last_bot_comment_idx: - continue - body = comment.get("body", "") - if "@openswe" in body.lower(): - relevant_comments.append(comment) - relevant_comments.extend(comments[i + 1 :]) - break + relevant_comments = get_recent_comments(comments, bot_message_prefixes) if relevant_comments: comments_text = "\n\n## Comments:\n" - for comment in recent_user_comments: + for comment in relevant_comments: author = comment.get("user", {}).get("name", "Unknown") body = comment.get("body", "") body_image_urls = extract_image_urls(body)