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>
This commit is contained in:
0xallam
2026-04-25 15:17:46 -07:00
co-authored by Claude Opus 4.7
parent 6990fd4ef1
commit b3f7cfd040
22 changed files with 257 additions and 320 deletions
-10
View File
@@ -1,10 +0,0 @@
from .tools import list_requests, repeat_request, scope_rules, send_request, view_request
__all__ = [
"list_requests",
"repeat_request",
"scope_rules",
"send_request",
"view_request",
]
+81 -30
View File
@@ -12,6 +12,7 @@ Tools: ``list_requests``, ``view_request``, ``send_request``,
from __future__ import annotations
import dataclasses
import json
import re
import time
from dataclasses import is_dataclass
@@ -19,7 +20,7 @@ from datetime import datetime
from typing import TYPE_CHECKING, Any, Literal
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
from agents import RunContextWrapper
from agents import RunContextWrapper, function_tool
from caido_sdk_client.types import (
ConnectionInfoInput,
CreateReplaySessionFromRaw,
@@ -30,8 +31,6 @@ from caido_sdk_client.types import (
UpdateScopeOptions,
)
from strix.tools._decorator import dump_tool_result, strix_tool
if TYPE_CHECKING:
from caido_sdk_client import Client
@@ -92,18 +91,20 @@ def _serialize(value: Any) -> Any:
def _no_client() -> str:
return dump_tool_result(
return json.dumps(
{
"success": False,
"error": "Caido client not initialized in context.",
},
ensure_ascii=False,
default=str,
)
# ----------------------------------------------------------------------
# list_requests
# ----------------------------------------------------------------------
@strix_tool(timeout=120)
@function_tool(timeout=120)
async def list_requests(
ctx: RunContextWrapper,
httpql_filter: str | None = None,
@@ -201,7 +202,7 @@ async def list_requests(
},
)
return dump_tool_result(
return json.dumps(
{
"success": True,
"entries": entries,
@@ -212,15 +213,21 @@ async def list_requests(
"end_cursor": connection.page_info.end_cursor,
},
},
ensure_ascii=False,
default=str,
)
except Exception as exc: # noqa: BLE001
return dump_tool_result({"success": False, "error": f"list_requests failed: {exc}"})
return json.dumps(
{"success": False, "error": f"list_requests failed: {exc}"},
ensure_ascii=False,
default=str,
)
# ----------------------------------------------------------------------
# view_request
# ----------------------------------------------------------------------
@strix_tool(timeout=60)
@function_tool(timeout=60)
async def view_request(
ctx: RunContextWrapper,
request_id: str,
@@ -265,8 +272,10 @@ async def view_request(
)
result = await client.request.get(request_id, opts)
if result is None:
return dump_tool_result(
return json.dumps(
{"success": False, "error": f"Request {request_id} not found"},
ensure_ascii=False,
default=str,
)
raw_bytes = (
@@ -275,20 +284,30 @@ async def view_request(
else (result.response.raw if result.response is not None else None)
)
if raw_bytes is None:
return dump_tool_result(
return json.dumps(
{
"success": False,
"error": f"No raw {part} for {request_id}",
},
ensure_ascii=False,
default=str,
)
content = raw_bytes.decode("utf-8", errors="replace")
if search_pattern:
return dump_tool_result(_regex_hits(content, search_pattern))
return json.dumps(_regex_hits(content, search_pattern), ensure_ascii=False, default=str)
return dump_tool_result(_paginate_lines(content, page=page, page_size=page_size))
return json.dumps(
_paginate_lines(content, page=page, page_size=page_size),
ensure_ascii=False,
default=str,
)
except Exception as exc: # noqa: BLE001
return dump_tool_result({"success": False, "error": f"view_request failed: {exc}"})
return json.dumps(
{"success": False, "error": f"view_request failed: {exc}"},
ensure_ascii=False,
default=str,
)
def _regex_hits(content: str, pattern: str) -> dict[str, Any]:
@@ -333,7 +352,7 @@ def _paginate_lines(content: str, *, page: int, page_size: int) -> dict[str, Any
# ----------------------------------------------------------------------
# send_request
# ----------------------------------------------------------------------
@strix_tool(timeout=120, strict_mode=False)
@function_tool(timeout=120, strict_mode=False)
async def send_request(
ctx: RunContextWrapper,
method: str,
@@ -367,13 +386,17 @@ async def send_request(
)
return await _replay_send(client, raw=raw, connection=connection)
except Exception as exc: # noqa: BLE001
return dump_tool_result({"success": False, "error": f"send_request failed: {exc}"})
return json.dumps(
{"success": False, "error": f"send_request failed: {exc}"},
ensure_ascii=False,
default=str,
)
# ----------------------------------------------------------------------
# repeat_request
# ----------------------------------------------------------------------
@strix_tool(timeout=120, strict_mode=False)
@function_tool(timeout=120, strict_mode=False)
async def repeat_request(
ctx: RunContextWrapper,
request_id: str,
@@ -412,8 +435,10 @@ async def repeat_request(
try:
result = await client.request.get(request_id, RequestGetOptions(request_raw=True))
if result is None or result.request.raw is None:
return dump_tool_result(
return json.dumps(
{"success": False, "error": f"Request {request_id} not found"},
ensure_ascii=False,
default=str,
)
original = result.request
@@ -429,13 +454,17 @@ async def repeat_request(
)
return await _replay_send(client, raw=raw, connection=connection)
except Exception as exc: # noqa: BLE001
return dump_tool_result({"success": False, "error": f"repeat_request failed: {exc}"})
return json.dumps(
{"success": False, "error": f"repeat_request failed: {exc}"},
ensure_ascii=False,
default=str,
)
# ----------------------------------------------------------------------
# scope_rules
# ----------------------------------------------------------------------
@strix_tool(timeout=60)
@function_tool(timeout=60)
async def scope_rules(
ctx: RunContextWrapper,
action: ScopeAction,
@@ -488,20 +517,28 @@ async def scope_rules(
try:
if action == "list":
scopes = await client.scope.list()
return dump_tool_result(
return json.dumps(
{"success": True, "scopes": [_serialize(s) for s in scopes]},
ensure_ascii=False,
default=str,
)
if action == "get":
if not scope_id:
return dump_tool_result(
return json.dumps(
{"success": False, "error": "scope_id required for get"},
ensure_ascii=False,
default=str,
)
scope = await client.scope.get(scope_id)
return dump_tool_result({"success": True, "scope": _serialize(scope)})
return json.dumps(
{"success": True, "scope": _serialize(scope)}, ensure_ascii=False, default=str
)
if action == "create":
if not scope_name:
return dump_tool_result(
return json.dumps(
{"success": False, "error": "scope_name required for create"},
ensure_ascii=False,
default=str,
)
scope = await client.scope.create(
CreateScopeOptions(
@@ -510,14 +547,18 @@ async def scope_rules(
denylist=list(denylist or []),
),
)
return dump_tool_result({"success": True, "scope": _serialize(scope)})
return json.dumps(
{"success": True, "scope": _serialize(scope)}, ensure_ascii=False, default=str
)
if action == "update":
if not scope_id or not scope_name:
return dump_tool_result(
return json.dumps(
{
"success": False,
"error": "scope_id and scope_name required for update",
},
ensure_ascii=False,
default=str,
)
scope = await client.scope.update(
scope_id,
@@ -527,16 +568,24 @@ async def scope_rules(
denylist=list(denylist or []),
),
)
return dump_tool_result({"success": True, "scope": _serialize(scope)})
return json.dumps(
{"success": True, "scope": _serialize(scope)}, ensure_ascii=False, default=str
)
# action == "delete" — exhaustive Literal
if not scope_id:
return dump_tool_result(
return json.dumps(
{"success": False, "error": "scope_id required for delete"},
ensure_ascii=False,
default=str,
)
await client.scope.delete(scope_id)
return dump_tool_result({"success": True, "deleted": scope_id})
return json.dumps({"success": True, "deleted": scope_id}, ensure_ascii=False, default=str)
except Exception as exc: # noqa: BLE001
return dump_tool_result({"success": False, "error": f"scope_rules failed: {exc}"})
return json.dumps(
{"success": False, "error": f"scope_rules failed: {exc}"},
ensure_ascii=False,
default=str,
)
# ----------------------------------------------------------------------
@@ -670,7 +719,7 @@ async def _replay_send(
"raw": response_raw.decode("utf-8", errors="replace"),
}
return dump_tool_result(
return json.dumps(
{
"success": result.status == "DONE",
"status": result.status,
@@ -679,4 +728,6 @@ async def _replay_send(
"elapsed_ms": elapsed_ms,
"response": response,
},
ensure_ascii=False,
default=str,
)