This commit is contained in:
0xhis 2026-05-03 15:50:28 -07:00 • committed by GitHub
commit fb50b8c7d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 67 additions and 50 deletions

View file

@ -1,5 +1,7 @@
from typing import Any
import html
from strix.agents.base_agent import BaseAgent
from strix.llm.config import LLMConfig
@ -97,55 +99,78 @@ class StrixAgent(BaseAgent):
elif target_type == "ip_address":
ip_addresses.append(details["target_ip"])
task_parts = []
target_lines = []
if repositories:
task_parts.append("\n\nRepositories:")
for repo in repositories:
if repo["workspace_path"]:
task_parts.append(f"- {repo['url']} (available at: {repo['workspace_path']})")
target_lines.append(
f' <target type="repository">{html.escape(repo["url"])} (code at: {html.escape(repo["workspace_path"])})</target>'
)
else:
task_parts.append(f"- {repo['url']}")
target_lines.append(
f' <target type="repository">{html.escape(repo["url"])}</target>'
)
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' <target type="local_code">{html.escape(code["path"])} (code at: {html.escape(code["workspace_path"])})</target>'
)
if urls:
task_parts.append("\n\nURLs:")
task_parts.extend(f"- {url}" for url in urls)
for url in urls:
target_lines.append(f' <target type="url">{html.escape(url)}</target>')
if ip_addresses:
task_parts.append("\n\nIP Addresses:")
task_parts.extend(f"- {ip}" for ip in ip_addresses)
for ip in ip_addresses:
target_lines.append(f' <target type="ip">{html.escape(ip)}</target>')
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)"
diff_scope_section = ""
if diff_scope.get("active"):
task_parts.append("\n\nScope Constraints:")
task_parts.append(
"- Pull request diff-scope mode is active. Prioritize changed files "
"and use other files only for context."
scope_lines = ["<diff_scope>"]
scope_lines.append(
" <note>Pull request diff-scope mode is active. Prioritize changed files "
"and use other files only for context.</note>"
)
for repo_scope in diff_scope.get("repos", []):
repo_label = (
repo_label = html.escape(
repo_scope.get("workspace_subdir")
or repo_scope.get("source_path")
or "repository"
)
changed_count = repo_scope.get("analyzable_files_count", 0)
deleted_count = repo_scope.get("deleted_files_count", 0)
task_parts.append(
f"- {repo_label}: {changed_count} changed file(s) in primary scope"
scope_lines.append(
f' <repo name="{repo_label}">{changed_count} changed file(s) in primary scope</repo>'
)
if deleted_count:
task_parts.append(
f"- {repo_label}: {deleted_count} deleted file(s) are context-only"
scope_lines.append(
f' <repo name="{repo_label}">{deleted_count} deleted file(s) are context-only</repo>'
)
scope_lines.append("</diff_scope>")
diff_scope_section = "\n" + "\n".join(scope_lines) + "\n"
task_description = " ".join(task_parts)
task_description = (
f"<scan_task>\n"
f"<targets>\n{targets_block}\n</targets>\n"
f"<mode>{mode}</mode>\n"
f"<action>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.</action>\n"
f"{diff_scope_section}</scan_task>"
)
if user_instructions:
task_description += f"\n\nSpecial instructions: {user_instructions}"
task_description += f"\n\nSpecial instructions: {html.escape(user_instructions)}"
return await self.agent_loop(task=task_description)

View file

@ -1,5 +1,6 @@
import asyncio
import contextlib
import html
import logging
from typing import TYPE_CHECKING, Any, Optional
@ -411,6 +412,13 @@ class BaseAgent(metaclass=AgentMeta):
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:
@ -485,33 +493,18 @@ class BaseAgent(metaclass=AgentMeta):
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"""<inter_agent_message>
<delivery_notice>
<important>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.</important>
</delivery_notice>
<sender>
<agent_name>{sender_name}</agent_name>
<agent_id>{sender_id}</agent_id>
</sender>
<message_metadata>
<type>{message.get("message_type", "information")}</type>
<priority>{message.get("priority", "normal")}</priority>
<timestamp>{message.get("timestamp", "")}</timestamp>
</message_metadata>
<content>
{message.get("content", "")}
</content>
<delivery_info>
<note>This message was delivered during your task execution.
Please acknowledge and respond if needed.</note>
</delivery_info>
</inter_agent_message>"""
content = message.get("content", "")
message_content = f"""<agent_message
from="{html.escape(sender_name)}"
id="{html.escape(str(sender_id))}"
type="{html.escape(message.get("message_type", "information"))}"
priority="{html.escape(message.get("priority", "normal"))}">
<![CDATA[{content}]]>
</agent_message>"""
state.add_message("user", message_content.strip())
message["read"] = True

View file

@ -44,9 +44,7 @@ class AgentState(BaseModel):
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

View file

@ -150,6 +150,7 @@ def clean_content(content: str) -> str:
hidden_xml_patterns = [
r"<inter_agent_message>.*?</inter_agent_message>",
r"<agent_message\b[^>]*>.*?</agent_message>",
r"<agent_completion_report>.*?</agent_completion_report>",
]
for pattern in hidden_xml_patterns: