strix-agent 0.4.0__py3-none-any.whl
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.
- strix/__init__.py +0 -0
- strix/agents/StrixAgent/__init__.py +4 -0
- strix/agents/StrixAgent/strix_agent.py +89 -0
- strix/agents/StrixAgent/system_prompt.jinja +404 -0
- strix/agents/__init__.py +10 -0
- strix/agents/base_agent.py +518 -0
- strix/agents/state.py +163 -0
- strix/interface/__init__.py +4 -0
- strix/interface/assets/tui_styles.tcss +694 -0
- strix/interface/cli.py +230 -0
- strix/interface/main.py +500 -0
- strix/interface/tool_components/__init__.py +39 -0
- strix/interface/tool_components/agents_graph_renderer.py +123 -0
- strix/interface/tool_components/base_renderer.py +62 -0
- strix/interface/tool_components/browser_renderer.py +120 -0
- strix/interface/tool_components/file_edit_renderer.py +99 -0
- strix/interface/tool_components/finish_renderer.py +31 -0
- strix/interface/tool_components/notes_renderer.py +108 -0
- strix/interface/tool_components/proxy_renderer.py +255 -0
- strix/interface/tool_components/python_renderer.py +34 -0
- strix/interface/tool_components/registry.py +72 -0
- strix/interface/tool_components/reporting_renderer.py +53 -0
- strix/interface/tool_components/scan_info_renderer.py +64 -0
- strix/interface/tool_components/terminal_renderer.py +131 -0
- strix/interface/tool_components/thinking_renderer.py +29 -0
- strix/interface/tool_components/user_message_renderer.py +43 -0
- strix/interface/tool_components/web_search_renderer.py +28 -0
- strix/interface/tui.py +1274 -0
- strix/interface/utils.py +559 -0
- strix/llm/__init__.py +15 -0
- strix/llm/config.py +20 -0
- strix/llm/llm.py +465 -0
- strix/llm/memory_compressor.py +212 -0
- strix/llm/request_queue.py +87 -0
- strix/llm/utils.py +87 -0
- strix/prompts/README.md +64 -0
- strix/prompts/__init__.py +109 -0
- strix/prompts/cloud/.gitkeep +0 -0
- strix/prompts/coordination/root_agent.jinja +41 -0
- strix/prompts/custom/.gitkeep +0 -0
- strix/prompts/frameworks/fastapi.jinja +142 -0
- strix/prompts/frameworks/nextjs.jinja +126 -0
- strix/prompts/protocols/graphql.jinja +215 -0
- strix/prompts/reconnaissance/.gitkeep +0 -0
- strix/prompts/technologies/firebase_firestore.jinja +177 -0
- strix/prompts/technologies/supabase.jinja +189 -0
- strix/prompts/vulnerabilities/authentication_jwt.jinja +147 -0
- strix/prompts/vulnerabilities/broken_function_level_authorization.jinja +146 -0
- strix/prompts/vulnerabilities/business_logic.jinja +171 -0
- strix/prompts/vulnerabilities/csrf.jinja +174 -0
- strix/prompts/vulnerabilities/idor.jinja +195 -0
- strix/prompts/vulnerabilities/information_disclosure.jinja +222 -0
- strix/prompts/vulnerabilities/insecure_file_uploads.jinja +188 -0
- strix/prompts/vulnerabilities/mass_assignment.jinja +141 -0
- strix/prompts/vulnerabilities/open_redirect.jinja +177 -0
- strix/prompts/vulnerabilities/path_traversal_lfi_rfi.jinja +142 -0
- strix/prompts/vulnerabilities/race_conditions.jinja +164 -0
- strix/prompts/vulnerabilities/rce.jinja +154 -0
- strix/prompts/vulnerabilities/sql_injection.jinja +151 -0
- strix/prompts/vulnerabilities/ssrf.jinja +135 -0
- strix/prompts/vulnerabilities/subdomain_takeover.jinja +155 -0
- strix/prompts/vulnerabilities/xss.jinja +169 -0
- strix/prompts/vulnerabilities/xxe.jinja +184 -0
- strix/runtime/__init__.py +19 -0
- strix/runtime/docker_runtime.py +399 -0
- strix/runtime/runtime.py +29 -0
- strix/runtime/tool_server.py +205 -0
- strix/telemetry/__init__.py +4 -0
- strix/telemetry/tracer.py +337 -0
- strix/tools/__init__.py +64 -0
- strix/tools/agents_graph/__init__.py +16 -0
- strix/tools/agents_graph/agents_graph_actions.py +621 -0
- strix/tools/agents_graph/agents_graph_actions_schema.xml +226 -0
- strix/tools/argument_parser.py +121 -0
- strix/tools/browser/__init__.py +4 -0
- strix/tools/browser/browser_actions.py +236 -0
- strix/tools/browser/browser_actions_schema.xml +183 -0
- strix/tools/browser/browser_instance.py +533 -0
- strix/tools/browser/tab_manager.py +342 -0
- strix/tools/executor.py +305 -0
- strix/tools/file_edit/__init__.py +4 -0
- strix/tools/file_edit/file_edit_actions.py +141 -0
- strix/tools/file_edit/file_edit_actions_schema.xml +128 -0
- strix/tools/finish/__init__.py +4 -0
- strix/tools/finish/finish_actions.py +174 -0
- strix/tools/finish/finish_actions_schema.xml +45 -0
- strix/tools/notes/__init__.py +14 -0
- strix/tools/notes/notes_actions.py +191 -0
- strix/tools/notes/notes_actions_schema.xml +150 -0
- strix/tools/proxy/__init__.py +20 -0
- strix/tools/proxy/proxy_actions.py +101 -0
- strix/tools/proxy/proxy_actions_schema.xml +267 -0
- strix/tools/proxy/proxy_manager.py +785 -0
- strix/tools/python/__init__.py +4 -0
- strix/tools/python/python_actions.py +47 -0
- strix/tools/python/python_actions_schema.xml +131 -0
- strix/tools/python/python_instance.py +172 -0
- strix/tools/python/python_manager.py +131 -0
- strix/tools/registry.py +196 -0
- strix/tools/reporting/__init__.py +6 -0
- strix/tools/reporting/reporting_actions.py +63 -0
- strix/tools/reporting/reporting_actions_schema.xml +30 -0
- strix/tools/terminal/__init__.py +4 -0
- strix/tools/terminal/terminal_actions.py +35 -0
- strix/tools/terminal/terminal_actions_schema.xml +146 -0
- strix/tools/terminal/terminal_manager.py +151 -0
- strix/tools/terminal/terminal_session.py +447 -0
- strix/tools/thinking/__init__.py +4 -0
- strix/tools/thinking/thinking_actions.py +18 -0
- strix/tools/thinking/thinking_actions_schema.xml +52 -0
- strix/tools/web_search/__init__.py +4 -0
- strix/tools/web_search/web_search_actions.py +80 -0
- strix/tools/web_search/web_search_actions_schema.xml +83 -0
- strix_agent-0.4.0.dist-info/LICENSE +201 -0
- strix_agent-0.4.0.dist-info/METADATA +282 -0
- strix_agent-0.4.0.dist-info/RECORD +118 -0
- strix_agent-0.4.0.dist-info/WHEEL +4 -0
- strix_agent-0.4.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import UTC, datetime
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
_global_tracer: Optional["Tracer"] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_global_tracer() -> Optional["Tracer"]:
|
|
18
|
+
return _global_tracer
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def set_global_tracer(tracer: "Tracer") -> None:
|
|
22
|
+
global _global_tracer # noqa: PLW0603
|
|
23
|
+
_global_tracer = tracer
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Tracer:
|
|
27
|
+
def __init__(self, run_name: str | None = None):
|
|
28
|
+
self.run_name = run_name
|
|
29
|
+
self.run_id = run_name or f"run-{uuid4().hex[:8]}"
|
|
30
|
+
self.start_time = datetime.now(UTC).isoformat()
|
|
31
|
+
self.end_time: str | None = None
|
|
32
|
+
|
|
33
|
+
self.agents: dict[str, dict[str, Any]] = {}
|
|
34
|
+
self.tool_executions: dict[int, dict[str, Any]] = {}
|
|
35
|
+
self.chat_messages: list[dict[str, Any]] = []
|
|
36
|
+
|
|
37
|
+
self.vulnerability_reports: list[dict[str, Any]] = []
|
|
38
|
+
self.final_scan_result: str | None = None
|
|
39
|
+
|
|
40
|
+
self.scan_results: dict[str, Any] | None = None
|
|
41
|
+
self.scan_config: dict[str, Any] | None = None
|
|
42
|
+
self.run_metadata: dict[str, Any] = {
|
|
43
|
+
"run_id": self.run_id,
|
|
44
|
+
"run_name": self.run_name,
|
|
45
|
+
"start_time": self.start_time,
|
|
46
|
+
"end_time": None,
|
|
47
|
+
"targets": [],
|
|
48
|
+
"status": "running",
|
|
49
|
+
}
|
|
50
|
+
self._run_dir: Path | None = None
|
|
51
|
+
self._next_execution_id = 1
|
|
52
|
+
self._next_message_id = 1
|
|
53
|
+
self._saved_vuln_ids: set[str] = set()
|
|
54
|
+
|
|
55
|
+
self.vulnerability_found_callback: Callable[[str, str, str, str], None] | None = None
|
|
56
|
+
|
|
57
|
+
def set_run_name(self, run_name: str) -> None:
|
|
58
|
+
self.run_name = run_name
|
|
59
|
+
self.run_id = run_name
|
|
60
|
+
|
|
61
|
+
def get_run_dir(self) -> Path:
|
|
62
|
+
if self._run_dir is None:
|
|
63
|
+
runs_dir = Path.cwd() / "strix_runs"
|
|
64
|
+
runs_dir.mkdir(exist_ok=True)
|
|
65
|
+
|
|
66
|
+
run_dir_name = self.run_name if self.run_name else self.run_id
|
|
67
|
+
self._run_dir = runs_dir / run_dir_name
|
|
68
|
+
self._run_dir.mkdir(exist_ok=True)
|
|
69
|
+
|
|
70
|
+
return self._run_dir
|
|
71
|
+
|
|
72
|
+
def add_vulnerability_report(
|
|
73
|
+
self,
|
|
74
|
+
title: str,
|
|
75
|
+
content: str,
|
|
76
|
+
severity: str,
|
|
77
|
+
) -> str:
|
|
78
|
+
report_id = f"vuln-{len(self.vulnerability_reports) + 1:04d}"
|
|
79
|
+
|
|
80
|
+
report = {
|
|
81
|
+
"id": report_id,
|
|
82
|
+
"title": title.strip(),
|
|
83
|
+
"content": content.strip(),
|
|
84
|
+
"severity": severity.lower().strip(),
|
|
85
|
+
"timestamp": datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S UTC"),
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
self.vulnerability_reports.append(report)
|
|
89
|
+
logger.info(f"Added vulnerability report: {report_id} - {title}")
|
|
90
|
+
|
|
91
|
+
if self.vulnerability_found_callback:
|
|
92
|
+
self.vulnerability_found_callback(
|
|
93
|
+
report_id, title.strip(), content.strip(), severity.lower().strip()
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self.save_run_data()
|
|
97
|
+
return report_id
|
|
98
|
+
|
|
99
|
+
def set_final_scan_result(
|
|
100
|
+
self,
|
|
101
|
+
content: str,
|
|
102
|
+
success: bool = True,
|
|
103
|
+
) -> None:
|
|
104
|
+
self.final_scan_result = content.strip()
|
|
105
|
+
|
|
106
|
+
self.scan_results = {
|
|
107
|
+
"scan_completed": True,
|
|
108
|
+
"content": content,
|
|
109
|
+
"success": success,
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
logger.info(f"Set final scan result: success={success}")
|
|
113
|
+
self.save_run_data(mark_complete=True)
|
|
114
|
+
|
|
115
|
+
def log_agent_creation(
|
|
116
|
+
self, agent_id: str, name: str, task: str, parent_id: str | None = None
|
|
117
|
+
) -> None:
|
|
118
|
+
agent_data: dict[str, Any] = {
|
|
119
|
+
"id": agent_id,
|
|
120
|
+
"name": name,
|
|
121
|
+
"task": task,
|
|
122
|
+
"status": "running",
|
|
123
|
+
"parent_id": parent_id,
|
|
124
|
+
"created_at": datetime.now(UTC).isoformat(),
|
|
125
|
+
"updated_at": datetime.now(UTC).isoformat(),
|
|
126
|
+
"tool_executions": [],
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
self.agents[agent_id] = agent_data
|
|
130
|
+
|
|
131
|
+
def log_chat_message(
|
|
132
|
+
self,
|
|
133
|
+
content: str,
|
|
134
|
+
role: str,
|
|
135
|
+
agent_id: str | None = None,
|
|
136
|
+
metadata: dict[str, Any] | None = None,
|
|
137
|
+
) -> int:
|
|
138
|
+
message_id = self._next_message_id
|
|
139
|
+
self._next_message_id += 1
|
|
140
|
+
|
|
141
|
+
message_data = {
|
|
142
|
+
"message_id": message_id,
|
|
143
|
+
"content": content,
|
|
144
|
+
"role": role,
|
|
145
|
+
"agent_id": agent_id,
|
|
146
|
+
"timestamp": datetime.now(UTC).isoformat(),
|
|
147
|
+
"metadata": metadata or {},
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
self.chat_messages.append(message_data)
|
|
151
|
+
return message_id
|
|
152
|
+
|
|
153
|
+
def log_tool_execution_start(self, agent_id: str, tool_name: str, args: dict[str, Any]) -> int:
|
|
154
|
+
execution_id = self._next_execution_id
|
|
155
|
+
self._next_execution_id += 1
|
|
156
|
+
|
|
157
|
+
now = datetime.now(UTC).isoformat()
|
|
158
|
+
execution_data = {
|
|
159
|
+
"execution_id": execution_id,
|
|
160
|
+
"agent_id": agent_id,
|
|
161
|
+
"tool_name": tool_name,
|
|
162
|
+
"args": args,
|
|
163
|
+
"status": "running",
|
|
164
|
+
"result": None,
|
|
165
|
+
"timestamp": now,
|
|
166
|
+
"started_at": now,
|
|
167
|
+
"completed_at": None,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
self.tool_executions[execution_id] = execution_data
|
|
171
|
+
|
|
172
|
+
if agent_id in self.agents:
|
|
173
|
+
self.agents[agent_id]["tool_executions"].append(execution_id)
|
|
174
|
+
|
|
175
|
+
return execution_id
|
|
176
|
+
|
|
177
|
+
def update_tool_execution(
|
|
178
|
+
self, execution_id: int, status: str, result: Any | None = None
|
|
179
|
+
) -> None:
|
|
180
|
+
if execution_id in self.tool_executions:
|
|
181
|
+
self.tool_executions[execution_id]["status"] = status
|
|
182
|
+
self.tool_executions[execution_id]["result"] = result
|
|
183
|
+
self.tool_executions[execution_id]["completed_at"] = datetime.now(UTC).isoformat()
|
|
184
|
+
|
|
185
|
+
def update_agent_status(
|
|
186
|
+
self, agent_id: str, status: str, error_message: str | None = None
|
|
187
|
+
) -> None:
|
|
188
|
+
if agent_id in self.agents:
|
|
189
|
+
self.agents[agent_id]["status"] = status
|
|
190
|
+
self.agents[agent_id]["updated_at"] = datetime.now(UTC).isoformat()
|
|
191
|
+
if error_message:
|
|
192
|
+
self.agents[agent_id]["error_message"] = error_message
|
|
193
|
+
|
|
194
|
+
def set_scan_config(self, config: dict[str, Any]) -> None:
|
|
195
|
+
self.scan_config = config
|
|
196
|
+
self.run_metadata.update(
|
|
197
|
+
{
|
|
198
|
+
"targets": config.get("targets", []),
|
|
199
|
+
"user_instructions": config.get("user_instructions", ""),
|
|
200
|
+
"max_iterations": config.get("max_iterations", 200),
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
self.get_run_dir()
|
|
204
|
+
|
|
205
|
+
def save_run_data(self, mark_complete: bool = False) -> None:
|
|
206
|
+
try:
|
|
207
|
+
run_dir = self.get_run_dir()
|
|
208
|
+
if mark_complete:
|
|
209
|
+
self.end_time = datetime.now(UTC).isoformat()
|
|
210
|
+
|
|
211
|
+
if self.final_scan_result:
|
|
212
|
+
penetration_test_report_file = run_dir / "penetration_test_report.md"
|
|
213
|
+
with penetration_test_report_file.open("w", encoding="utf-8") as f:
|
|
214
|
+
f.write("# Security Penetration Test Report\n\n")
|
|
215
|
+
f.write(
|
|
216
|
+
f"**Generated:** {datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S UTC')}\n\n"
|
|
217
|
+
)
|
|
218
|
+
f.write(f"{self.final_scan_result}\n")
|
|
219
|
+
logger.info(
|
|
220
|
+
f"Saved final penetration test report to: {penetration_test_report_file}"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if self.vulnerability_reports:
|
|
224
|
+
vuln_dir = run_dir / "vulnerabilities"
|
|
225
|
+
vuln_dir.mkdir(exist_ok=True)
|
|
226
|
+
|
|
227
|
+
new_reports = [
|
|
228
|
+
report
|
|
229
|
+
for report in self.vulnerability_reports
|
|
230
|
+
if report["id"] not in self._saved_vuln_ids
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
for report in new_reports:
|
|
234
|
+
vuln_file = vuln_dir / f"{report['id']}.md"
|
|
235
|
+
with vuln_file.open("w", encoding="utf-8") as f:
|
|
236
|
+
f.write(f"# {report['title']}\n\n")
|
|
237
|
+
f.write(f"**ID:** {report['id']}\n")
|
|
238
|
+
f.write(f"**Severity:** {report['severity'].upper()}\n")
|
|
239
|
+
f.write(f"**Found:** {report['timestamp']}\n\n")
|
|
240
|
+
f.write("## Description\n\n")
|
|
241
|
+
f.write(f"{report['content']}\n")
|
|
242
|
+
self._saved_vuln_ids.add(report["id"])
|
|
243
|
+
|
|
244
|
+
if self.vulnerability_reports:
|
|
245
|
+
severity_order = {"critical": 0, "high": 1, "medium": 2, "low": 3, "info": 4}
|
|
246
|
+
sorted_reports = sorted(
|
|
247
|
+
self.vulnerability_reports,
|
|
248
|
+
key=lambda x: (severity_order.get(x["severity"], 5), x["timestamp"]),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
vuln_csv_file = run_dir / "vulnerabilities.csv"
|
|
252
|
+
with vuln_csv_file.open("w", encoding="utf-8", newline="") as f:
|
|
253
|
+
import csv
|
|
254
|
+
|
|
255
|
+
fieldnames = ["id", "title", "severity", "timestamp", "file"]
|
|
256
|
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
|
257
|
+
writer.writeheader()
|
|
258
|
+
|
|
259
|
+
for report in sorted_reports:
|
|
260
|
+
writer.writerow(
|
|
261
|
+
{
|
|
262
|
+
"id": report["id"],
|
|
263
|
+
"title": report["title"],
|
|
264
|
+
"severity": report["severity"].upper(),
|
|
265
|
+
"timestamp": report["timestamp"],
|
|
266
|
+
"file": f"vulnerabilities/{report['id']}.md",
|
|
267
|
+
}
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
if new_reports:
|
|
271
|
+
logger.info(
|
|
272
|
+
f"Saved {len(new_reports)} new vulnerability report(s) to: {vuln_dir}"
|
|
273
|
+
)
|
|
274
|
+
logger.info(f"Updated vulnerability index: {vuln_csv_file}")
|
|
275
|
+
|
|
276
|
+
logger.info(f"📊 Essential scan data saved to: {run_dir}")
|
|
277
|
+
|
|
278
|
+
except (OSError, RuntimeError):
|
|
279
|
+
logger.exception("Failed to save scan data")
|
|
280
|
+
|
|
281
|
+
def _calculate_duration(self) -> float:
|
|
282
|
+
try:
|
|
283
|
+
start = datetime.fromisoformat(self.start_time.replace("Z", "+00:00"))
|
|
284
|
+
if self.end_time:
|
|
285
|
+
end = datetime.fromisoformat(self.end_time.replace("Z", "+00:00"))
|
|
286
|
+
return (end - start).total_seconds()
|
|
287
|
+
except (ValueError, TypeError):
|
|
288
|
+
pass
|
|
289
|
+
return 0.0
|
|
290
|
+
|
|
291
|
+
def get_agent_tools(self, agent_id: str) -> list[dict[str, Any]]:
|
|
292
|
+
return [
|
|
293
|
+
exec_data
|
|
294
|
+
for exec_data in self.tool_executions.values()
|
|
295
|
+
if exec_data.get("agent_id") == agent_id
|
|
296
|
+
]
|
|
297
|
+
|
|
298
|
+
def get_real_tool_count(self) -> int:
|
|
299
|
+
return sum(
|
|
300
|
+
1
|
|
301
|
+
for exec_data in self.tool_executions.values()
|
|
302
|
+
if exec_data.get("tool_name") not in ["scan_start_info", "subagent_start_info"]
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
def get_total_llm_stats(self) -> dict[str, Any]:
|
|
306
|
+
from strix.tools.agents_graph.agents_graph_actions import _agent_instances
|
|
307
|
+
|
|
308
|
+
total_stats = {
|
|
309
|
+
"input_tokens": 0,
|
|
310
|
+
"output_tokens": 0,
|
|
311
|
+
"cached_tokens": 0,
|
|
312
|
+
"cache_creation_tokens": 0,
|
|
313
|
+
"cost": 0.0,
|
|
314
|
+
"requests": 0,
|
|
315
|
+
"failed_requests": 0,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for agent_instance in _agent_instances.values():
|
|
319
|
+
if hasattr(agent_instance, "llm") and hasattr(agent_instance.llm, "_total_stats"):
|
|
320
|
+
agent_stats = agent_instance.llm._total_stats
|
|
321
|
+
total_stats["input_tokens"] += agent_stats.input_tokens
|
|
322
|
+
total_stats["output_tokens"] += agent_stats.output_tokens
|
|
323
|
+
total_stats["cached_tokens"] += agent_stats.cached_tokens
|
|
324
|
+
total_stats["cache_creation_tokens"] += agent_stats.cache_creation_tokens
|
|
325
|
+
total_stats["cost"] += agent_stats.cost
|
|
326
|
+
total_stats["requests"] += agent_stats.requests
|
|
327
|
+
total_stats["failed_requests"] += agent_stats.failed_requests
|
|
328
|
+
|
|
329
|
+
total_stats["cost"] = round(total_stats["cost"], 4)
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
"total": total_stats,
|
|
333
|
+
"total_tokens": total_stats["input_tokens"] + total_stats["output_tokens"],
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
def cleanup(self) -> None:
|
|
337
|
+
self.save_run_data(mark_complete=True)
|
strix/tools/__init__.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from .executor import (
|
|
4
|
+
execute_tool,
|
|
5
|
+
execute_tool_invocation,
|
|
6
|
+
execute_tool_with_validation,
|
|
7
|
+
extract_screenshot_from_result,
|
|
8
|
+
process_tool_invocations,
|
|
9
|
+
remove_screenshot_from_result,
|
|
10
|
+
validate_tool_availability,
|
|
11
|
+
)
|
|
12
|
+
from .registry import (
|
|
13
|
+
ImplementedInClientSideOnlyError,
|
|
14
|
+
get_tool_by_name,
|
|
15
|
+
get_tool_names,
|
|
16
|
+
get_tools_prompt,
|
|
17
|
+
needs_agent_state,
|
|
18
|
+
register_tool,
|
|
19
|
+
tools,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
SANDBOX_MODE = os.getenv("STRIX_SANDBOX_MODE", "false").lower() == "true"
|
|
24
|
+
|
|
25
|
+
HAS_PERPLEXITY_API = bool(os.getenv("PERPLEXITY_API_KEY"))
|
|
26
|
+
|
|
27
|
+
if not SANDBOX_MODE:
|
|
28
|
+
from .agents_graph import * # noqa: F403
|
|
29
|
+
from .browser import * # noqa: F403
|
|
30
|
+
from .file_edit import * # noqa: F403
|
|
31
|
+
from .finish import * # noqa: F403
|
|
32
|
+
from .notes import * # noqa: F403
|
|
33
|
+
from .proxy import * # noqa: F403
|
|
34
|
+
from .python import * # noqa: F403
|
|
35
|
+
from .reporting import * # noqa: F403
|
|
36
|
+
from .terminal import * # noqa: F403
|
|
37
|
+
from .thinking import * # noqa: F403
|
|
38
|
+
|
|
39
|
+
if HAS_PERPLEXITY_API:
|
|
40
|
+
from .web_search import * # noqa: F403
|
|
41
|
+
else:
|
|
42
|
+
from .browser import * # noqa: F403
|
|
43
|
+
from .file_edit import * # noqa: F403
|
|
44
|
+
from .notes import * # noqa: F403
|
|
45
|
+
from .proxy import * # noqa: F403
|
|
46
|
+
from .python import * # noqa: F403
|
|
47
|
+
from .terminal import * # noqa: F403
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"ImplementedInClientSideOnlyError",
|
|
51
|
+
"execute_tool",
|
|
52
|
+
"execute_tool_invocation",
|
|
53
|
+
"execute_tool_with_validation",
|
|
54
|
+
"extract_screenshot_from_result",
|
|
55
|
+
"get_tool_by_name",
|
|
56
|
+
"get_tool_names",
|
|
57
|
+
"get_tools_prompt",
|
|
58
|
+
"needs_agent_state",
|
|
59
|
+
"process_tool_invocations",
|
|
60
|
+
"register_tool",
|
|
61
|
+
"remove_screenshot_from_result",
|
|
62
|
+
"tools",
|
|
63
|
+
"validate_tool_availability",
|
|
64
|
+
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .agents_graph_actions import (
|
|
2
|
+
agent_finish,
|
|
3
|
+
create_agent,
|
|
4
|
+
send_message_to_agent,
|
|
5
|
+
view_agent_graph,
|
|
6
|
+
wait_for_message,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"agent_finish",
|
|
12
|
+
"create_agent",
|
|
13
|
+
"send_message_to_agent",
|
|
14
|
+
"view_agent_graph",
|
|
15
|
+
"wait_for_message",
|
|
16
|
+
]
|