fix: address audit findings — SDK plumbing, TUI bus, dead code

Critical fixes:

- ``StrixOrchestrationHooks.on_agent_start`` now finds the
  ``CaidoCapability`` via ``ctx.context['caido_capability']`` instead
  of ``agent.capabilities`` (we use plain ``Agent``, not
  ``SandboxAgent``, so the latter never existed). The session
  manager's bundle already exposes the capability; ``run_strix_scan``
  threads it through ``make_agent_context`` and ``create_agent``
  forwards it to children.

- ``run_strix_scan`` registers the ``StrixTracingProcessor`` with the
  SDK's tracing provider via ``add_trace_processor`` so SDK trace
  spans hit ``run_dir/events.jsonl`` (was previously a parallel stream
  the SDK ignored).

- ``on_llm_end`` now writes to ``Tracer.record_llm_usage`` in
  addition to ``bus.record_usage`` so the CLI/TUI stats panel sees
  real numbers instead of zeros.

- ``run_strix_scan`` accepts an externally-built ``AgentMessageBus``
  + an explicit ``model`` arg. The TUI pre-creates the bus so its
  stop and chat-input handlers can submit ``bus.send`` /
  ``bus.cancel_descendants`` coroutines onto the scan thread's loop
  via ``asyncio.run_coroutine_threadsafe`` — replacing the
  TODO-stub no-ops.

- ``model`` config now propagates root → context → child agents in
  ``create_agent`` (was hardcoded fallback).

Dead-code removal:

- Deleted the ``load_skill`` tool entirely (host module, sandbox
  module, TUI renderer, tests). The legacy implementation reached
  into a global ``_agent_instances`` registry that no longer exists;
  the post-migration stub returned ``success=True`` without
  injecting anything — pure theater. Skills are still preloaded via
  the system prompt at scan-bring-up.

- Dropped ``tenacity`` and ``xmltodict`` from
  ``[project.dependencies]`` — neither is imported anywhere
  post-migration.

- Stripped the system prompt's "use the load_skill tool" lines.

Tests: 278/278 passing. Removed two ``load_skill`` test cases and a
``test_tool_registration_modes::test_load_skill_import_...`` assertion
that exercised the deleted module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
0xallam
2026-04-25 10:08:35 -07:00
co-authored by Claude Opus 4.7
parent af42499b95
commit 572ef2a2af
21 changed files with 98 additions and 318 deletions
@@ -4,7 +4,6 @@ from . import (
browser_renderer,
file_edit_renderer,
finish_renderer,
load_skill_renderer,
notes_renderer,
proxy_renderer,
python_renderer,
@@ -29,7 +28,6 @@ __all__ = [
"file_edit_renderer",
"finish_renderer",
"get_tool_renderer",
"load_skill_renderer",
"notes_renderer",
"proxy_renderer",
"python_renderer",
@@ -1,33 +0,0 @@
from typing import Any, ClassVar
from rich.text import Text
from textual.widgets import Static
from .base_renderer import BaseToolRenderer
from .registry import register_tool_renderer
@register_tool_renderer
class LoadSkillRenderer(BaseToolRenderer):
tool_name: ClassVar[str] = "load_skill"
css_classes: ClassVar[list[str]] = ["tool-call", "load-skill-tool"]
@classmethod
def render(cls, tool_data: dict[str, Any]) -> Static:
args = tool_data.get("args", {})
status = tool_data.get("status", "completed")
requested = args.get("skills", "")
text = Text()
text.append("", style="#10b981")
text.append("loading skill", style="dim")
if requested:
text.append(" ")
text.append(requested, style="#10b981")
elif not tool_data.get("result"):
text.append("\n ")
text.append("Loading...", style="dim")
return Static(text, classes=cls.get_css_classes(status))
+31 -14
View File
@@ -711,6 +711,13 @@ class StrixTUIApp(App): # type: ignore[misc]
self.tracer.set_scan_config(self.scan_config)
set_global_tracer(self.tracer)
# Pre-create the bus here (rather than letting ``run_strix_scan``
# build its own) so the TUI can hold a handle for stop / chat
# routing while the scan loop runs in a worker thread.
from strix.orchestration.bus import AgentMessageBus
self.bus: AgentMessageBus = AgentMessageBus()
self.agent_nodes: dict[str, TreeNode] = {}
self._displayed_agents: set[str] = set()
@@ -720,6 +727,7 @@ class StrixTUIApp(App): # type: ignore[misc]
self._last_streaming_len: dict[str, int] = {}
self._scan_thread: threading.Thread | None = None
self._scan_loop: asyncio.AbstractEventLoop | None = None
self._scan_stop_event = threading.Event()
self._scan_completed = threading.Event()
@@ -1496,6 +1504,10 @@ class StrixTUIApp(App): # type: ignore[misc]
try:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# Stash the loop so synchronous TUI handlers (stop /
# chat) can submit bus coroutines onto it from the
# main thread.
self._scan_loop = loop
try:
if not self._scan_stop_event.is_set():
@@ -1508,6 +1520,7 @@ class StrixTUIApp(App): # type: ignore[misc]
image=str(image),
sources_path=sources_path,
tracer=self.tracer,
bus=self.bus,
interactive=True,
),
)
@@ -1827,10 +1840,6 @@ class StrixTUIApp(App): # type: ignore[misc]
metadata={"interrupted": True},
)
# TODO: route user→agent messages through the AgentMessageBus
# once the TUI has a handle on it. The bus currently lives
# inside ``run_strix_scan`` scope only.
if self.tracer:
self.tracer.log_chat_message(
content=message,
@@ -1838,11 +1847,18 @@ class StrixTUIApp(App): # type: ignore[misc]
agent_id=self.selected_agent_id,
)
logging.warning(
"User-message-to-agent dispatch is not wired post-migration; "
"message %r logged to tracer but not delivered.",
message,
)
# Route to the agent's bus inbox. The scan loop runs on a
# worker thread; ``run_coroutine_threadsafe`` submits the
# coroutine onto that loop and returns immediately so the TUI
# stays responsive.
if self._scan_loop is not None and not self._scan_loop.is_closed():
asyncio.run_coroutine_threadsafe(
self.bus.send(
self.selected_agent_id,
{"from": "user", "content": message, "type": "instruction"},
),
self._scan_loop,
)
self._displayed_events.clear()
self._update_chat_view()
@@ -1941,11 +1957,12 @@ class StrixTUIApp(App): # type: ignore[misc]
return agent_name, False
def action_confirm_stop_agent(self, agent_id: str) -> None:
# TODO: route to ``bus.cancel_descendants(agent_id)`` once the TUI
# has a handle on the AgentMessageBus.
logging.warning(
"Stop-agent dispatch is not wired post-migration; agent %s left running.",
agent_id,
if self._scan_loop is None or self._scan_loop.is_closed():
logging.warning("No active scan loop; cannot stop agent %s", agent_id)
return
asyncio.run_coroutine_threadsafe(
self.bus.cancel_descendants(agent_id),
self._scan_loop,
)
def action_custom_quit(self) -> None: