strix-agent 0.1.16__tar.gz → 0.1.17__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of strix-agent might be problematic. Click here for more details.
- {strix_agent-0.1.16 → strix_agent-0.1.17}/PKG-INFO +1 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/pyproject.toml +1 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/agents/base_agent.py +13 -2
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/app.py +37 -6
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/agents_graph_renderer.py +1 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/base_renderer.py +2 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/browser_renderer.py +3 -3
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/proxy_renderer.py +2 -2
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/registry.py +1 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/reporting_renderer.py +1 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/scan_info_renderer.py +8 -6
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/terminal_renderer.py +1 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tracer.py +5 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/llm/llm.py +48 -3
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/llm/request_queue.py +16 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/runtime/docker_runtime.py +2 -2
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/executor.py +1 -1
- {strix_agent-0.1.16 → strix_agent-0.1.17}/LICENSE +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/README.md +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/agents/StrixAgent/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/agents/StrixAgent/strix_agent.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/agents/StrixAgent/system_prompt.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/agents/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/agents/state.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/assets/cli.tcss +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/main.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/file_edit_renderer.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/finish_renderer.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/notes_renderer.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/python_renderer.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/thinking_renderer.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/user_message_renderer.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/web_search_renderer.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/llm/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/llm/config.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/llm/memory_compressor.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/llm/utils.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/coordination/root_agent.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/authentication_jwt.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/business_logic.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/csrf.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/idor.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/race_conditions.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/rce.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/sql_injection.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/ssrf.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/xss.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/xxe.jinja +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/runtime/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/runtime/runtime.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/runtime/tool_server.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/agents_graph/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/agents_graph/agents_graph_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/agents_graph/agents_graph_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/argument_parser.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/browser/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/browser/browser_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/browser/browser_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/browser/browser_instance.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/browser/tab_manager.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/file_edit/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/file_edit/file_edit_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/file_edit/file_edit_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/finish/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/finish/finish_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/finish/finish_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/notes/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/notes/notes_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/notes/notes_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/proxy/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/proxy/proxy_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/proxy/proxy_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/proxy/proxy_manager.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/python/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/python/python_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/python/python_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/python/python_instance.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/python/python_manager.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/registry.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/reporting/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/reporting/reporting_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/reporting/reporting_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/terminal/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/terminal/terminal_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/terminal/terminal_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/terminal/terminal_manager.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/terminal/terminal_session.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/thinking/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/thinking/thinking_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/thinking/thinking_actions_schema.xml +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/web_search/__init__.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/web_search/web_search_actions.py +0 -0
- {strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/web_search/web_search_actions_schema.xml +0 -0
|
@@ -181,10 +181,21 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
181
181
|
continue
|
|
182
182
|
|
|
183
183
|
except LLMRequestFailedError as e:
|
|
184
|
-
|
|
184
|
+
error_msg = str(e)
|
|
185
|
+
error_details = getattr(e, "details", None)
|
|
186
|
+
self.state.add_error(error_msg)
|
|
185
187
|
self.state.enter_waiting_state(llm_failed=True)
|
|
186
188
|
if tracer:
|
|
187
|
-
tracer.update_agent_status(self.state.agent_id, "llm_failed")
|
|
189
|
+
tracer.update_agent_status(self.state.agent_id, "llm_failed", error_msg)
|
|
190
|
+
if error_details:
|
|
191
|
+
tracer.log_tool_execution_start(
|
|
192
|
+
self.state.agent_id,
|
|
193
|
+
"llm_error_details",
|
|
194
|
+
{"error": error_msg, "details": error_details},
|
|
195
|
+
)
|
|
196
|
+
tracer.update_tool_execution(
|
|
197
|
+
tracer._next_execution_id - 1, "failed", error_details
|
|
198
|
+
)
|
|
188
199
|
continue
|
|
189
200
|
|
|
190
201
|
except (RuntimeError, ValueError, TypeError) as e:
|
|
@@ -9,6 +9,8 @@ import threading
|
|
|
9
9
|
from collections.abc import Callable
|
|
10
10
|
from typing import Any, ClassVar
|
|
11
11
|
|
|
12
|
+
from rich.markup import escape as rich_escape
|
|
13
|
+
from rich.text import Text
|
|
12
14
|
from textual import events, on
|
|
13
15
|
from textual.app import App, ComposeResult
|
|
14
16
|
from textual.binding import Binding
|
|
@@ -24,7 +26,7 @@ from strix.llm.config import LLMConfig
|
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
def escape_markup(text: str) -> str:
|
|
27
|
-
return text
|
|
29
|
+
return rich_escape(text)
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
class ChatTextArea(TextArea): # type: ignore[misc]
|
|
@@ -483,7 +485,7 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
483
485
|
self._displayed_events = current_event_ids
|
|
484
486
|
|
|
485
487
|
chat_display = self.query_one("#chat_display", Static)
|
|
486
|
-
|
|
488
|
+
self._update_static_content_safe(chat_display, content)
|
|
487
489
|
|
|
488
490
|
chat_display.set_classes(css_class)
|
|
489
491
|
|
|
@@ -546,11 +548,18 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
546
548
|
self._safe_widget_operation(keymap_indicator.update, "")
|
|
547
549
|
self._safe_widget_operation(status_display.remove_class, "hidden")
|
|
548
550
|
elif status == "llm_failed":
|
|
549
|
-
|
|
551
|
+
error_msg = agent_data.get("error_message", "")
|
|
552
|
+
display_msg = (
|
|
553
|
+
f"[red]{escape_markup(error_msg)}[/red]"
|
|
554
|
+
if error_msg
|
|
555
|
+
else "[red]LLM request failed[/red]"
|
|
556
|
+
)
|
|
557
|
+
self._safe_widget_operation(status_text.update, display_msg)
|
|
550
558
|
self._safe_widget_operation(
|
|
551
559
|
keymap_indicator.update, "[dim]Send message to retry[/dim]"
|
|
552
560
|
)
|
|
553
561
|
self._safe_widget_operation(status_display.remove_class, "hidden")
|
|
562
|
+
self._stop_dot_animation()
|
|
554
563
|
elif status == "waiting":
|
|
555
564
|
animated_text = self._get_animated_waiting_text(self.selected_agent_id)
|
|
556
565
|
self._safe_widget_operation(status_text.update, animated_text)
|
|
@@ -633,7 +642,7 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
633
642
|
|
|
634
643
|
for agent_id, agent_data in self.tracer.agents.items():
|
|
635
644
|
status = agent_data.get("status", "running")
|
|
636
|
-
if status in ["running", "waiting"
|
|
645
|
+
if status in ["running", "waiting"]:
|
|
637
646
|
has_active_agents = True
|
|
638
647
|
current_dots = self._agent_dot_states.get(agent_id, 0)
|
|
639
648
|
self._agent_dot_states[agent_id] = (current_dots + 1) % 4
|
|
@@ -644,7 +653,7 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
644
653
|
and self.selected_agent_id in self.tracer.agents
|
|
645
654
|
):
|
|
646
655
|
selected_status = self.tracer.agents[self.selected_agent_id].get("status", "running")
|
|
647
|
-
if selected_status in ["running", "waiting"
|
|
656
|
+
if selected_status in ["running", "waiting"]:
|
|
648
657
|
self._update_agent_status_display()
|
|
649
658
|
|
|
650
659
|
if not has_active_agents:
|
|
@@ -652,7 +661,7 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
652
661
|
for agent_id in list(self._agent_dot_states.keys()):
|
|
653
662
|
if agent_id not in self.tracer.agents or self.tracer.agents[agent_id].get(
|
|
654
663
|
"status"
|
|
655
|
-
) not in ["running", "waiting"
|
|
664
|
+
) not in ["running", "waiting"]:
|
|
656
665
|
del self._agent_dot_states[agent_id]
|
|
657
666
|
|
|
658
667
|
def _gather_agent_events(self, agent_id: str) -> list[dict[str, Any]]:
|
|
@@ -900,6 +909,7 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
900
909
|
"reporting_action": "#ea580c",
|
|
901
910
|
"scan_start_info": "#22c55e",
|
|
902
911
|
"subagent_start_info": "#22c55e",
|
|
912
|
+
"llm_error_details": "#dc2626",
|
|
903
913
|
}
|
|
904
914
|
|
|
905
915
|
color = tool_colors.get(tool_name, "#737373")
|
|
@@ -911,6 +921,14 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
911
921
|
if renderer:
|
|
912
922
|
widget = renderer.render(tool_data)
|
|
913
923
|
content = str(widget.renderable)
|
|
924
|
+
elif tool_name == "llm_error_details":
|
|
925
|
+
lines = ["[red]✗ LLM Request Failed[/red]"]
|
|
926
|
+
if args.get("details"):
|
|
927
|
+
details = args["details"]
|
|
928
|
+
if len(details) > 300:
|
|
929
|
+
details = details[:297] + "..."
|
|
930
|
+
lines.append(f"[dim]Details:[/dim] {escape_markup(details)}")
|
|
931
|
+
content = "\n".join(lines)
|
|
914
932
|
else:
|
|
915
933
|
status_icons = {
|
|
916
934
|
"running": "[yellow]●[/yellow]",
|
|
@@ -1127,6 +1145,19 @@ class StrixCLIApp(App): # type: ignore[misc]
|
|
|
1127
1145
|
else:
|
|
1128
1146
|
return True
|
|
1129
1147
|
|
|
1148
|
+
def _update_static_content_safe(self, widget: Static, content: str) -> None:
|
|
1149
|
+
try:
|
|
1150
|
+
widget.update(content)
|
|
1151
|
+
except Exception: # noqa: BLE001
|
|
1152
|
+
try:
|
|
1153
|
+
safe_text = Text.from_markup(content)
|
|
1154
|
+
widget.update(safe_text)
|
|
1155
|
+
except Exception: # noqa: BLE001
|
|
1156
|
+
import re
|
|
1157
|
+
|
|
1158
|
+
plain_text = re.sub(r"\[.*?\]", "", content)
|
|
1159
|
+
widget.update(plain_text)
|
|
1160
|
+
|
|
1130
1161
|
|
|
1131
1162
|
async def run_strix_cli(args: argparse.Namespace) -> None:
|
|
1132
1163
|
app = StrixCLIApp(args)
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/agents_graph_renderer.py
RENAMED
|
@@ -31,7 +31,7 @@ class CreateAgentRenderer(BaseToolRenderer):
|
|
|
31
31
|
task = args.get("task", "")
|
|
32
32
|
name = args.get("name", "Agent")
|
|
33
33
|
|
|
34
|
-
header = f"🤖 [bold #fbbf24]Creating {name}[/]"
|
|
34
|
+
header = f"🤖 [bold #fbbf24]Creating {cls.escape_markup(name)}[/]"
|
|
35
35
|
|
|
36
36
|
if task:
|
|
37
37
|
task_display = task[:400] + "..." if len(task) > 400 else task
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
2
|
from typing import Any, ClassVar
|
|
3
3
|
|
|
4
|
+
from rich.markup import escape as rich_escape
|
|
4
5
|
from textual.widgets import Static
|
|
5
6
|
|
|
6
7
|
|
|
@@ -16,7 +17,7 @@ class BaseToolRenderer(ABC):
|
|
|
16
17
|
|
|
17
18
|
@classmethod
|
|
18
19
|
def escape_markup(cls, text: str) -> str:
|
|
19
|
-
return text
|
|
20
|
+
return rich_escape(text)
|
|
20
21
|
|
|
21
22
|
@classmethod
|
|
22
23
|
def format_args(cls, args: dict[str, Any], max_length: int = 500) -> str:
|
|
@@ -76,7 +76,7 @@ class BrowserRenderer(BaseToolRenderer):
|
|
|
76
76
|
"double_click": "double clicking",
|
|
77
77
|
"hover": "hovering",
|
|
78
78
|
}
|
|
79
|
-
message = action_words[action]
|
|
79
|
+
message = cls.escape_markup(action_words[action])
|
|
80
80
|
|
|
81
81
|
return f"{browser_icon} [#06b6d4]{message}[/]"
|
|
82
82
|
|
|
@@ -97,9 +97,9 @@ class BrowserRenderer(BaseToolRenderer):
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
if action in simple_actions:
|
|
100
|
-
return f"{browser_icon} [#06b6d4]{simple_actions[action]}[/]"
|
|
100
|
+
return f"{browser_icon} [#06b6d4]{cls.escape_markup(simple_actions[action])}[/]"
|
|
101
101
|
|
|
102
|
-
return f"{browser_icon} [#06b6d4]{action}[/]"
|
|
102
|
+
return f"{browser_icon} [#06b6d4]{cls.escape_markup(action)}[/]"
|
|
103
103
|
|
|
104
104
|
@classmethod
|
|
105
105
|
def _format_url(cls, url: str) -> str:
|
|
@@ -64,7 +64,7 @@ class ViewRequestRenderer(BaseToolRenderer):
|
|
|
64
64
|
|
|
65
65
|
part = args.get("part", "request")
|
|
66
66
|
|
|
67
|
-
header = f"👀 [bold #06b6d4]Viewing {part}[/]"
|
|
67
|
+
header = f"👀 [bold #06b6d4]Viewing {cls.escape_markup(part)}[/]"
|
|
68
68
|
|
|
69
69
|
if result and isinstance(result, dict):
|
|
70
70
|
if "content" in result:
|
|
@@ -107,7 +107,7 @@ class SendRequestRenderer(BaseToolRenderer):
|
|
|
107
107
|
method = args.get("method", "GET")
|
|
108
108
|
url = args.get("url", "")
|
|
109
109
|
|
|
110
|
-
header = f"📤 [bold #06b6d4]Sending {method}[/]"
|
|
110
|
+
header = f"📤 [bold #06b6d4]Sending {cls.escape_markup(method)}[/]"
|
|
111
111
|
|
|
112
112
|
if result and isinstance(result, dict):
|
|
113
113
|
status_code = result.get("status_code")
|
|
@@ -54,7 +54,7 @@ def _render_default_tool_widget(tool_data: dict[str, Any]) -> Static:
|
|
|
54
54
|
|
|
55
55
|
status_text = BaseToolRenderer.get_status_icon(status)
|
|
56
56
|
|
|
57
|
-
header = f"→ Using tool [bold blue]{tool_name}[/]"
|
|
57
|
+
header = f"→ Using tool [bold blue]{BaseToolRenderer.escape_markup(tool_name)}[/]"
|
|
58
58
|
content_parts = [header]
|
|
59
59
|
|
|
60
60
|
args_str = BaseToolRenderer.format_args(args)
|
|
@@ -27,7 +27,7 @@ class CreateVulnerabilityReportRenderer(BaseToolRenderer):
|
|
|
27
27
|
if severity:
|
|
28
28
|
severity_color = cls._get_severity_color(severity.lower())
|
|
29
29
|
content_parts.append(
|
|
30
|
-
f" [dim]Severity: [{severity_color}]{severity.upper()}[/{severity_color}][/]"
|
|
30
|
+
f" [dim]Severity: [{severity_color}]{cls.escape_markup(severity.upper())}[/{severity_color}][/]"
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
if content:
|
|
@@ -28,12 +28,12 @@ class ScanStartInfoRenderer(BaseToolRenderer):
|
|
|
28
28
|
@classmethod
|
|
29
29
|
def _build_target_display(cls, target: dict[str, Any]) -> str:
|
|
30
30
|
if target_url := target.get("target_url"):
|
|
31
|
-
return
|
|
31
|
+
return cls.escape_markup(str(target_url))
|
|
32
32
|
if target_repo := target.get("target_repo"):
|
|
33
|
-
return
|
|
33
|
+
return cls.escape_markup(str(target_repo))
|
|
34
34
|
if target_path := target.get("target_path"):
|
|
35
|
-
return
|
|
36
|
-
return "
|
|
35
|
+
return cls.escape_markup(str(target_path))
|
|
36
|
+
return "unknown target"
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
@register_tool_renderer
|
|
@@ -49,9 +49,11 @@ class SubagentStartInfoRenderer(BaseToolRenderer):
|
|
|
49
49
|
name = args.get("name", "Unknown Agent")
|
|
50
50
|
task = args.get("task", "")
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
name = cls.escape_markup(str(name))
|
|
53
|
+
content = f"🤖 Spawned subagent {name}"
|
|
53
54
|
if task:
|
|
54
|
-
|
|
55
|
+
task = cls.escape_markup(str(task))
|
|
56
|
+
content += f"\n Task: {task}"
|
|
55
57
|
|
|
56
58
|
css_classes = cls.get_css_classes(status)
|
|
57
59
|
return Static(content, classes=css_classes)
|
|
@@ -111,7 +111,7 @@ class TerminalRenderer(BaseToolRenderer):
|
|
|
111
111
|
)
|
|
112
112
|
|
|
113
113
|
if is_special:
|
|
114
|
-
return f"{terminal_icon} [#ef4444]{command}[/]"
|
|
114
|
+
return f"{terminal_icon} [#ef4444]{cls.escape_markup(command)}[/]"
|
|
115
115
|
|
|
116
116
|
if is_input:
|
|
117
117
|
formatted_command = cls._format_command_display(command)
|
|
@@ -168,10 +168,14 @@ class Tracer:
|
|
|
168
168
|
self.tool_executions[execution_id]["result"] = result
|
|
169
169
|
self.tool_executions[execution_id]["completed_at"] = datetime.now(UTC).isoformat()
|
|
170
170
|
|
|
171
|
-
def update_agent_status(
|
|
171
|
+
def update_agent_status(
|
|
172
|
+
self, agent_id: str, status: str, error_message: str | None = None
|
|
173
|
+
) -> None:
|
|
172
174
|
if agent_id in self.agents:
|
|
173
175
|
self.agents[agent_id]["status"] = status
|
|
174
176
|
self.agents[agent_id]["updated_at"] = datetime.now(UTC).isoformat()
|
|
177
|
+
if error_message:
|
|
178
|
+
self.agents[agent_id]["error_message"] = error_message
|
|
175
179
|
|
|
176
180
|
def set_scan_config(self, config: dict[str, Any]) -> None:
|
|
177
181
|
self.scan_config = config
|
|
@@ -30,7 +30,10 @@ if api_key:
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class LLMRequestFailedError(Exception):
|
|
33
|
-
|
|
33
|
+
def __init__(self, message: str, details: str | None = None):
|
|
34
|
+
super().__init__(message)
|
|
35
|
+
self.message = message
|
|
36
|
+
self.details = details
|
|
34
37
|
|
|
35
38
|
|
|
36
39
|
MODELS_WITHOUT_STOP_WORDS = [
|
|
@@ -211,7 +214,7 @@ class LLM:
|
|
|
211
214
|
|
|
212
215
|
return cached_messages
|
|
213
216
|
|
|
214
|
-
async def generate(
|
|
217
|
+
async def generate( # noqa: PLR0912, PLR0915
|
|
215
218
|
self,
|
|
216
219
|
conversation_history: list[dict[str, Any]],
|
|
217
220
|
scan_id: str | None = None,
|
|
@@ -255,8 +258,50 @@ class LLM:
|
|
|
255
258
|
tool_invocations=tool_invocations if tool_invocations else None,
|
|
256
259
|
)
|
|
257
260
|
|
|
261
|
+
except litellm.RateLimitError as e:
|
|
262
|
+
raise LLMRequestFailedError("LLM request failed: Rate limit exceeded", str(e)) from e
|
|
263
|
+
except litellm.AuthenticationError as e:
|
|
264
|
+
raise LLMRequestFailedError("LLM request failed: Invalid API key", str(e)) from e
|
|
265
|
+
except litellm.NotFoundError as e:
|
|
266
|
+
raise LLMRequestFailedError("LLM request failed: Model not found", str(e)) from e
|
|
267
|
+
except litellm.ContextWindowExceededError as e:
|
|
268
|
+
raise LLMRequestFailedError("LLM request failed: Context too long", str(e)) from e
|
|
269
|
+
except litellm.ContentPolicyViolationError as e:
|
|
270
|
+
raise LLMRequestFailedError(
|
|
271
|
+
"LLM request failed: Content policy violation", str(e)
|
|
272
|
+
) from e
|
|
273
|
+
except litellm.ServiceUnavailableError as e:
|
|
274
|
+
raise LLMRequestFailedError("LLM request failed: Service unavailable", str(e)) from e
|
|
275
|
+
except litellm.Timeout as e:
|
|
276
|
+
raise LLMRequestFailedError("LLM request failed: Request timed out", str(e)) from e
|
|
277
|
+
except litellm.UnprocessableEntityError as e:
|
|
278
|
+
raise LLMRequestFailedError("LLM request failed: Unprocessable entity", str(e)) from e
|
|
279
|
+
except litellm.InternalServerError as e:
|
|
280
|
+
raise LLMRequestFailedError("LLM request failed: Internal server error", str(e)) from e
|
|
281
|
+
except litellm.APIConnectionError as e:
|
|
282
|
+
raise LLMRequestFailedError("LLM request failed: Connection error", str(e)) from e
|
|
283
|
+
except litellm.UnsupportedParamsError as e:
|
|
284
|
+
raise LLMRequestFailedError("LLM request failed: Unsupported parameters", str(e)) from e
|
|
285
|
+
except litellm.BudgetExceededError as e:
|
|
286
|
+
raise LLMRequestFailedError("LLM request failed: Budget exceeded", str(e)) from e
|
|
287
|
+
except litellm.APIResponseValidationError as e:
|
|
288
|
+
raise LLMRequestFailedError(
|
|
289
|
+
"LLM request failed: Response validation error", str(e)
|
|
290
|
+
) from e
|
|
291
|
+
except litellm.JSONSchemaValidationError as e:
|
|
292
|
+
raise LLMRequestFailedError(
|
|
293
|
+
"LLM request failed: JSON schema validation error", str(e)
|
|
294
|
+
) from e
|
|
295
|
+
except litellm.InvalidRequestError as e:
|
|
296
|
+
raise LLMRequestFailedError("LLM request failed: Invalid request", str(e)) from e
|
|
297
|
+
except litellm.BadRequestError as e:
|
|
298
|
+
raise LLMRequestFailedError("LLM request failed: Bad request", str(e)) from e
|
|
299
|
+
except litellm.APIError as e:
|
|
300
|
+
raise LLMRequestFailedError("LLM request failed: API error", str(e)) from e
|
|
301
|
+
except litellm.OpenAIError as e:
|
|
302
|
+
raise LLMRequestFailedError("LLM request failed: OpenAI error", str(e)) from e
|
|
258
303
|
except Exception as e:
|
|
259
|
-
raise LLMRequestFailedError("LLM request failed
|
|
304
|
+
raise LLMRequestFailedError(f"LLM request failed: {type(e).__name__}", str(e)) from e
|
|
260
305
|
|
|
261
306
|
@property
|
|
262
307
|
def usage_stats(self) -> dict[str, dict[str, int | float]]:
|
|
@@ -4,13 +4,27 @@ import threading
|
|
|
4
4
|
import time
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
import litellm
|
|
7
8
|
from litellm import ModelResponse, completion
|
|
8
|
-
from tenacity import retry, stop_after_attempt, wait_exponential
|
|
9
|
+
from tenacity import retry, retry_if_exception, stop_after_attempt, wait_exponential
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
logger = logging.getLogger(__name__)
|
|
12
13
|
|
|
13
14
|
|
|
15
|
+
def should_retry_exception(exception: Exception) -> bool:
|
|
16
|
+
status_code = None
|
|
17
|
+
|
|
18
|
+
if hasattr(exception, "status_code"):
|
|
19
|
+
status_code = exception.status_code
|
|
20
|
+
elif hasattr(exception, "response") and hasattr(exception.response, "status_code"):
|
|
21
|
+
status_code = exception.response.status_code
|
|
22
|
+
|
|
23
|
+
if status_code is not None:
|
|
24
|
+
return bool(litellm._should_retry(status_code))
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
|
|
14
28
|
class LLMRequestQueue:
|
|
15
29
|
def __init__(self, max_concurrent: int = 6, delay_between_requests: float = 1.0):
|
|
16
30
|
self.max_concurrent = max_concurrent
|
|
@@ -40,6 +54,7 @@ class LLMRequestQueue:
|
|
|
40
54
|
@retry( # type: ignore[misc]
|
|
41
55
|
stop=stop_after_attempt(5),
|
|
42
56
|
wait=wait_exponential(multiplier=2, min=1, max=30),
|
|
57
|
+
retry=retry_if_exception(should_retry_exception),
|
|
43
58
|
reraise=True,
|
|
44
59
|
)
|
|
45
60
|
async def _reliable_request(self, completion_args: dict[str, Any]) -> ModelResponse:
|
|
@@ -320,7 +320,7 @@ class DockerRuntime(AbstractRuntime):
|
|
|
320
320
|
import httpx
|
|
321
321
|
|
|
322
322
|
try:
|
|
323
|
-
async with httpx.AsyncClient() as client:
|
|
323
|
+
async with httpx.AsyncClient(trust_env=False) as client:
|
|
324
324
|
response = await client.post(
|
|
325
325
|
f"{api_url}/register_agent",
|
|
326
326
|
params={"agent_id": agent_id},
|
|
@@ -337,7 +337,7 @@ class DockerRuntime(AbstractRuntime):
|
|
|
337
337
|
container = self.client.containers.get(container_id)
|
|
338
338
|
container.reload()
|
|
339
339
|
|
|
340
|
-
host = "
|
|
340
|
+
host = "127.0.0.1"
|
|
341
341
|
if "DOCKER_HOST" in os.environ:
|
|
342
342
|
docker_host = os.environ["DOCKER_HOST"]
|
|
343
343
|
if "://" in docker_host:
|
|
@@ -62,7 +62,7 @@ async def _execute_tool_in_sandbox(tool_name: str, agent_state: Any, **kwargs: A
|
|
|
62
62
|
"Content-Type": "application/json",
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
async with httpx.AsyncClient() as client:
|
|
65
|
+
async with httpx.AsyncClient(trust_env=False) as client:
|
|
66
66
|
try:
|
|
67
67
|
response = await client.post(
|
|
68
68
|
request_url, json=request_data, headers=headers, timeout=None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/cli/tool_components/user_message_renderer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/authentication_jwt.jinja
RENAMED
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/business_logic.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/prompts/vulnerabilities/race_conditions.jinja
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/agents_graph/agents_graph_actions_schema.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/file_edit/file_edit_actions_schema.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/reporting/reporting_actions_schema.xml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{strix_agent-0.1.16 → strix_agent-0.1.17}/strix/tools/web_search/web_search_actions_schema.xml
RENAMED
|
File without changes
|