Files
usestrix__strix/strix/tools/finish/tool.py
T
0xallam b3f7cfd040 refactor: nuke `strix_tool` shim + dead package re-exports
``@strix_tool`` was passing through every kwarg to ``@function_tool``
with the same defaults — zero Strix-specific value-add. The docstring
also still claimed terminal/browser/python tools opted into
``timeout_behavior="raise_exception"``, but those tools were all
deleted in the recent migrations.

- Replace 30 ``@strix_tool(...)`` callsites with ``@function_tool(...)``.
- Inline ``dump_tool_result(x)`` as ``json.dumps(x, ensure_ascii=False,
  default=str)`` at all 64 callsites — no helper.
- Delete ``strix/tools/_decorator.py``.

Drive-by: gut dead package re-exports.

- ``strix/{agents,orchestration,tools}/__init__.py`` re-exported
  symbols nobody imports via the package — every consumer uses deep
  paths (``from strix.agents.factory import build_strix_agent``).
- The 8 ``strix/tools/<sub>/__init__.py`` re-exports only fed the
  splat ``from .agents_graph import *`` etc. in the parent package
  init, which is also gone now.
- Reduced to docstrings (or empty) so ``import strix.tools`` doesn't
  drag every tool's transitive deps in eagerly.

Drive-by: drop dead helpers in ``runtime.session_manager``
(``cached_scan_ids``, ``_reset_cache_for_tests``) — zero callers since
``tests/`` was nuked in ``a6d578c``.

Verified all tool timeouts preserved (think=10, list_requests=120,
finish_scan=60, web_search=330) and ruff/mypy at baseline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 15:17:46 -07:00

142 lines
5.3 KiB
Python

"""``finish_scan`` — root-agent termination + executive report persistence."""
from __future__ import annotations
import asyncio
import json
import logging
from typing import Any
from agents import RunContextWrapper, function_tool
logger = logging.getLogger(__name__)
def _do_finish(
*,
parent_id: str | None,
executive_summary: str,
methodology: str,
technical_analysis: str,
recommendations: str,
) -> dict[str, Any]:
if parent_id is not None:
return {
"success": False,
"error": "finish_scan_wrong_agent",
"message": "This tool can only be used by the root/main agent",
"suggestion": "If you are a subagent, use agent_finish instead",
}
errors: list[str] = []
if not executive_summary.strip():
errors.append("Executive summary cannot be empty")
if not methodology.strip():
errors.append("Methodology cannot be empty")
if not technical_analysis.strip():
errors.append("Technical analysis cannot be empty")
if not recommendations.strip():
errors.append("Recommendations cannot be empty")
if errors:
return {"success": False, "message": "Validation failed", "errors": errors}
try:
from strix.telemetry.tracer import get_global_tracer
tracer = get_global_tracer()
if tracer is None:
logger.warning("No global tracer; scan results not persisted")
return {
"success": True,
"scan_completed": True,
"message": "Scan completed (not persisted)",
"warning": "Results could not be persisted - tracer unavailable",
}
tracer.update_scan_final_fields(
executive_summary=executive_summary.strip(),
methodology=methodology.strip(),
technical_analysis=technical_analysis.strip(),
recommendations=recommendations.strip(),
)
return {
"success": True,
"scan_completed": True,
"message": "Scan completed successfully",
"vulnerabilities_found": len(tracer.vulnerability_reports),
}
except (ImportError, AttributeError) as e:
return {"success": False, "message": f"Failed to complete scan: {e!s}"}
@function_tool(timeout=60)
async def finish_scan(
ctx: RunContextWrapper,
executive_summary: str,
methodology: str,
technical_analysis: str,
recommendations: str,
) -> str:
"""Finalize the scan — persist the customer-facing report.
**Root-agent only.** Subagents must call ``agent_finish`` from the
multi-agent graph tools instead. Calling this finalizes everything:
1. Verifies you are the root agent.
2. Writes the four narrative sections to the scan record.
3. Marks the scan completed and stops execution.
**Pre-flight checklist:**
- All vulnerabilities you found are filed via
``create_vulnerability_report`` (un-reported findings are not
tracked and not credited).
- All subagents have terminated. If any are still ``running`` /
``stopping``, message them or use ``wait_for_message``.
- Don't double-report — one report per distinct vulnerability.
**Calling this multiple times overwrites the previous report.**
Make the single call comprehensive.
**Customer-facing report rules** (this output is rendered into the
final PDF the client sees):
- Never mention internal infrastructure: no local/absolute paths
(``/workspace/...``), no agent names, no sandbox/orchestrator/
tooling references, no system prompts, no model-internal errors.
- Tone: formal, third-person, objective, concise. This is a
consultant deliverable, not an engineering log.
- Each section has a specific role:
- ``executive_summary`` — for non-technical leadership. Risk
posture, business impact (data exposure / compliance /
reputation), notable criticals, overarching remediation
theme.
- ``methodology`` — frameworks followed (OWASP WSTG, PTES,
OSSTMM, NIST), engagement type (black/gray/white box), scope
and constraints, categories of testing performed. **No**
internal execution detail.
- ``technical_analysis`` — consolidated findings overview with
severity model and systemic root causes. Reference individual
vuln reports for repro steps; don't duplicate raw evidence.
- ``recommendations`` — prioritized actions grouped by urgency
(Immediate / Short-term / Medium-term), each with concrete
remediation steps. End with retest/validation guidance.
Args:
executive_summary: Business-level summary for leadership.
methodology: Frameworks, scope, and approach.
technical_analysis: Consolidated findings + systemic themes.
recommendations: Prioritized, actionable remediation.
"""
inner = ctx.context if isinstance(ctx.context, dict) else {}
result = await asyncio.to_thread(
_do_finish,
parent_id=inner.get("parent_id"),
executive_summary=executive_summary,
methodology=methodology,
technical_analysis=technical_analysis,
recommendations=recommendations,
)
return json.dumps(result, ensure_ascii=False, default=str)