From d317572f5b5905d1e633da4db9ad46b0f06d51fb Mon Sep 17 00:00:00 2001 From: Ousama Ben Younes Date: Fri, 10 Jul 2026 23:13:03 +0000 Subject: [PATCH 1/2] fix: strip chatgpt transcript tags from agent messages --- strix/core/agents.py | 21 +++++++++++++++++++-- tests/test_agent_messages.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/test_agent_messages.py diff --git a/strix/core/agents.py b/strix/core/agents.py index c96204df..dd117b45 100644 --- a/strix/core/agents.py +++ b/strix/core/agents.py @@ -5,6 +5,7 @@ from __future__ import annotations import asyncio import json import logging +import re import tempfile from dataclasses import dataclass, field from pathlib import Path @@ -29,6 +30,22 @@ Status = Literal["running", "waiting", "completed", "stopped", "crashed", "faile # on other agents is re-checked on a timer. WaitKind = Literal["user", "agents", "stalled"] +_CHATGPT_TRANSCRIPT_TAGS = ( + "analysis", + "assistant", + "channel", + "final", + "user", +) +_CHATGPT_TRANSCRIPT_TAG_RE = re.compile( + rf"", + flags=re.IGNORECASE, +) + + +def _strip_chatgpt_transcript_tags(content: str) -> str: + return _CHATGPT_TRANSCRIPT_TAG_RE.sub("", content) + @dataclass(slots=True) class AgentRuntime: @@ -372,7 +389,7 @@ class AgentCoordinator: await self._maybe_snapshot() async def cancel_descendants(self, agent_id: str) -> None: - tasks = [] + tasks: list[asyncio.Task[Any]] = [] async with self._lock: for aid in reversed(self._subtree_order_locked(agent_id)): task = self.runtimes.get(aid, AgentRuntime()).task @@ -437,7 +454,7 @@ class AgentCoordinator: def _message_to_session_item(self, message: dict[str, Any]) -> TResponseInputItem: sender = str(message.get("from", "unknown")) - content = str(message.get("content", "")) + content = _strip_chatgpt_transcript_tags(str(message.get("content", ""))) if sender == "user": return cast("TResponseInputItem", {"role": "user", "content": content}) sender_name = self.names.get(sender, sender) diff --git a/tests/test_agent_messages.py b/tests/test_agent_messages.py new file mode 100644 index 00000000..1594c47e --- /dev/null +++ b/tests/test_agent_messages.py @@ -0,0 +1,35 @@ +"""Tests for agent-to-session message conversion.""" + +from __future__ import annotations + +from strix.core.agents import AgentCoordinator + + +CHATGPT_TRANSCRIPT_CONTENT = ( + "checked target\nfinal\ndone" +) + + +def test_message_to_session_item_strips_chatgpt_transcript_tags() -> None: + coordinator = AgentCoordinator() + coordinator.names["child"] = "Researcher" + + item = coordinator._message_to_session_item( + { + "from": "child", + "type": "information", + "priority": "normal", + "content": CHATGPT_TRANSCRIPT_CONTENT, + } + ) + + content = str(item["content"]) + + assert "" not in content + assert "" not in content + assert "" not in content + assert "" not in content + assert "" not in content + assert "" not in content + assert "checked target" in content + assert "done" in content From 907fbe624a5f8d5c6ee0a33ae03095c518da8467 Mon Sep 17 00:00:00 2001 From: Ousama Ben Younes Date: Sat, 11 Jul 2026 00:30:25 +0000 Subject: [PATCH 2/2] fix: address transcript tag review feedback Preserve literal transcript-like tags in user prompts. Strip attributed and self-closing transcript tags from non-user session messages. RED->GREEN: new regression tests fail with the production fix reverted, then pass with 3 passed. Full suite: 145 passed, 2 warnings. --- strix/core/agents.py | 7 ++++-- tests/test_agent_messages.py | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/strix/core/agents.py b/strix/core/agents.py index dd117b45..bba2bc54 100644 --- a/strix/core/agents.py +++ b/strix/core/agents.py @@ -37,8 +37,10 @@ _CHATGPT_TRANSCRIPT_TAGS = ( "final", "user", ) +_CHATGPT_TRANSCRIPT_TAG_ALTERNATION = "|".join(re.escape(tag) for tag in _CHATGPT_TRANSCRIPT_TAGS) +_OPTIONAL_TAG_ATTRIBUTES_PATTERN = r"(?:\s+[^>]*)?" _CHATGPT_TRANSCRIPT_TAG_RE = re.compile( - rf"", + rf"", flags=re.IGNORECASE, ) @@ -454,9 +456,10 @@ class AgentCoordinator: def _message_to_session_item(self, message: dict[str, Any]) -> TResponseInputItem: sender = str(message.get("from", "unknown")) - content = _strip_chatgpt_transcript_tags(str(message.get("content", ""))) + content = str(message.get("content", "")) if sender == "user": return cast("TResponseInputItem", {"role": "user", "content": content}) + content = _strip_chatgpt_transcript_tags(content) sender_name = self.names.get(sender, sender) msg_type = message.get("type", "information") priority = message.get("priority", "normal") diff --git a/tests/test_agent_messages.py b/tests/test_agent_messages.py index 1594c47e..76e8f2d0 100644 --- a/tests/test_agent_messages.py +++ b/tests/test_agent_messages.py @@ -8,6 +8,16 @@ from strix.core.agents import AgentCoordinator CHATGPT_TRANSCRIPT_CONTENT = ( "checked target\nfinal\ndone" ) +CHATGPT_TRANSCRIPT_CONTENT_WITH_ATTRIBUTES = ( + 'checked target\n' + 'final\n' + 'done\n' + "" +) +USER_LITERAL_TAG_CONTENT = ( + "Please preserve this XML-like snippet: " + 'literal user content and done' +) def test_message_to_session_item_strips_chatgpt_transcript_tags() -> None: @@ -33,3 +43,39 @@ def test_message_to_session_item_strips_chatgpt_transcript_tags() -> None: assert "" not in content assert "checked target" in content assert "done" in content + + +def test_message_to_session_item_preserves_user_literal_tags() -> None: + coordinator = AgentCoordinator() + + item = coordinator._message_to_session_item( + { + "from": "user", + "content": USER_LITERAL_TAG_CONTENT, + } + ) + + assert item["content"] == USER_LITERAL_TAG_CONTENT + + +def test_message_to_session_item_strips_attributed_chatgpt_transcript_tags() -> None: + coordinator = AgentCoordinator() + coordinator.names["child"] = "Researcher" + + item = coordinator._message_to_session_item( + { + "from": "child", + "type": "information", + "priority": "normal", + "content": CHATGPT_TRANSCRIPT_CONTENT_WITH_ATTRIBUTES, + } + ) + + content = str(item["content"]) + + assert '' not in content + assert '' not in content + assert '' not in content + assert "" not in content + assert "checked target" in content + assert "done" in content