diff --git a/strix/agents/StrixAgent/strix_agent.py b/strix/agents/StrixAgent/strix_agent.py
index 1519ed512..e76ef2a8d 100644
--- a/strix/agents/StrixAgent/strix_agent.py
+++ b/strix/agents/StrixAgent/strix_agent.py
@@ -1,5 +1,7 @@
from typing import Any
+import html
+
from strix.agents.base_agent import BaseAgent
from strix.llm.config import LLMConfig
@@ -57,31 +59,45 @@ async def execute_scan(self, scan_config: dict[str, Any]) -> dict[str, Any]: #
elif target_type == "ip_address":
ip_addresses.append(details["target_ip"])
- task_parts = []
+ target_lines = []
- if repositories:
- task_parts.append("\n\nRepositories:")
+if repositories:
for repo in repositories:
if repo["workspace_path"]:
- task_parts.append(f"- {repo['url']} (available at: {repo['workspace_path']})")
+ target_lines.append(f' {html.escape(repo["url"])} (code at: {html.escape(repo["workspace_path"])})')
else:
- task_parts.append(f"- {repo['url']}")
+ target_lines.append(f' {html.escape(repo["url"])}')
if local_code:
- task_parts.append("\n\nLocal Codebases:")
- task_parts.extend(
- f"- {code['path']} (available at: {code['workspace_path']})" for code in local_code
- )
+ for code in local_code:
+ target_lines.append(f' {html.escape(code["path"])} (code at: {html.escape(code["workspace_path"])})')
if urls:
- task_parts.append("\n\nURLs:")
- task_parts.extend(f"- {url}" for url in urls)
+ for url in urls:
+ target_lines.append(f' {html.escape(url)}')
if ip_addresses:
- task_parts.append("\n\nIP Addresses:")
- task_parts.extend(f"- {ip}" for ip in ip_addresses)
-
- task_description = " ".join(task_parts)
+ for ip in ip_addresses:
+ target_lines.append(f' {html.escape(ip)}')
+
+ targets_block = "\n".join(target_lines)
+
+ has_code = bool(repositories or local_code)
+ has_urls = bool(urls or ip_addresses)
+ if has_code and has_urls:
+ mode = "COMBINED MODE (code + deployed target)"
+ elif has_code:
+ mode = "WHITE-BOX (source code provided)"
+ else:
+ mode = "BLACK-BOX (URL/domain targets)"
+
+ task_description = (
+ f"\n"
+ f"\n{targets_block}\n\n"
+ f"{mode}\n"
+ f"Begin security assessment NOW. Your first tool call must be create_agent to spawn context-gathering subagents for the targets listed above. Do NOT call wait_for_message — the targets are already specified.\n"
+ f""
+ )
if user_instructions:
task_description += f"\n\nSpecial instructions: {user_instructions}"
diff --git a/strix/agents/base_agent.py b/strix/agents/base_agent.py
index 74fe21ef5..e5e191046 100644
--- a/strix/agents/base_agent.py
+++ b/strix/agents/base_agent.py
@@ -1,5 +1,6 @@
import asyncio
import contextlib
+import html
import logging
from typing import TYPE_CHECKING, Any, Optional
@@ -410,6 +411,13 @@ async def _process_iteration(self, tracer: Optional["Tracer"]) -> bool | None:
if actions:
return await self._execute_actions(actions, tracer)
+ corrective_message = (
+ "You responded with plain text instead of a tool call. "
+ "While the agent loop is running, EVERY response MUST be a tool call. "
+ "Do NOT send plain text messages. Act via your available tools. "
+ "Review your task and take action now."
+ )
+ self.state.add_message("user", corrective_message)
return None
async def _execute_actions(self, actions: list[Any], tracer: Optional["Tracer"]) -> bool:
@@ -484,33 +492,18 @@ def _check_agent_messages(self, state: AgentState) -> None: # noqa: PLR0912
sender_name = "User"
state.add_message("user", message.get("content", ""))
else:
+ sender_name = sender_id or "Unknown"
if sender_id and sender_id in _agent_graph.get("nodes", {}):
sender_name = _agent_graph["nodes"][sender_id]["name"]
- message_content = f"""
-
- You have received a message from another agent. You should acknowledge
- this message and respond appropriately based on its content. However, DO NOT echo
- back or repeat the entire message structure in your response. Simply process the
- content and respond naturally as/if needed.
-
-
- {sender_name}
- {sender_id}
-
-
- {message.get("message_type", "information")}
- {message.get("priority", "normal")}
- {message.get("timestamp", "")}
-
-
-{message.get("content", "")}
-
-
- This message was delivered during your task execution.
- Please acknowledge and respond if needed.
-
-"""
+ content = message.get("content", "")
+ message_content = f"""
+
+"""
state.add_message("user", message_content.strip())
message["read"] = True
diff --git a/strix/agents/state.py b/strix/agents/state.py
index da04ee7f9..5e84d0f50 100644
--- a/strix/agents/state.py
+++ b/strix/agents/state.py
@@ -44,9 +44,7 @@ def increment_iteration(self) -> None:
self.iteration += 1
self.last_updated = datetime.now(UTC).isoformat()
- def add_message(
- self, role: str, content: Any, thinking_blocks: list[dict[str, Any]] | None = None
- ) -> None:
+ def add_message(self, role: str, content: Any, thinking_blocks: list | None = None) -> None:
message = {"role": role, "content": content}
if thinking_blocks:
message["thinking_blocks"] = thinking_blocks
diff --git a/strix/llm/utils.py b/strix/llm/utils.py
index cb61a81ed..b3da25164 100644
--- a/strix/llm/utils.py
+++ b/strix/llm/utils.py
@@ -150,6 +150,7 @@ def clean_content(content: str) -> str:
hidden_xml_patterns = [
r".*?",
+ r"]*>.*?",
r".*?",
]
for pattern in hidden_xml_patterns: