From 525333290695fad8847530318eaebf9e458dd1fc Mon Sep 17 00:00:00 2001 From: 0xallam Date: Sat, 25 Apr 2026 18:08:36 -0700 Subject: [PATCH] fix(telemetry): capture tool args in tool_executions for TUI renderers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 19 tool renderers under strix/interface/tool_components/ all read tool_data.get("args", {}) to render meaningful previews (URLs, methods, note titles, vuln severities, etc.). After the SDK migration, tracer.log_tool_start was only recording tool_name — every renderer silently fell back to its empty-args path and the TUI lost its per-call context. Pull args from the SDK-native ToolContext (tool_input when parsed, otherwise json-decode tool_arguments) and stash them on the tool_executions entry. log_tool_start now takes an optional args dict; existing callers pass nothing and get the empty-dict default. Co-Authored-By: Claude Opus 4.7 (1M context) --- strix/orchestration/hooks.py | 25 +++++++++++++++++++++++-- strix/telemetry/tracer.py | 8 +++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/strix/orchestration/hooks.py b/strix/orchestration/hooks.py index 39055b8..4f83339 100644 --- a/strix/orchestration/hooks.py +++ b/strix/orchestration/hooks.py @@ -2,6 +2,7 @@ from __future__ import annotations +import json import logging from datetime import UTC, datetime from typing import Any @@ -9,6 +10,7 @@ from typing import Any from agents.items import ModelResponse from agents.lifecycle import RunHooks from agents.run_context import AgentHookContext, RunContextWrapper +from agents.tool_context import ToolContext logger = logging.getLogger(__name__) @@ -219,8 +221,27 @@ class StrixOrchestrationHooks(RunHooks[Any]): if not isinstance(ctx, dict): return tracer = ctx.get("tracer") - if tracer is not None: - tracer.log_tool_start(ctx.get("agent_id", "?"), tool.name) + if tracer is None: + return + # ``context`` is a ``ToolContext`` for function-tool calls (per the + # SDK ``RunHooks.on_tool_start`` docstring) — that's where the + # per-call args live. ``tool_input`` is the parsed dict when the + # SDK has it; otherwise parse ``tool_arguments`` (raw JSON). + args: dict[str, Any] = {} + if isinstance(context, ToolContext): + tool_input = context.tool_input + if isinstance(tool_input, dict): + args = tool_input + else: + raw = context.tool_arguments + if raw: + try: + parsed = json.loads(raw) + except (ValueError, TypeError): + parsed = None + if isinstance(parsed, dict): + args = parsed + tracer.log_tool_start(ctx.get("agent_id", "?"), tool.name, args) except Exception: logger.exception("on_tool_start failed") diff --git a/strix/telemetry/tracer.py b/strix/telemetry/tracer.py index 48f81a0..0a6daaf 100644 --- a/strix/telemetry/tracer.py +++ b/strix/telemetry/tracer.py @@ -249,13 +249,19 @@ class Tracer: final_scan_result=self.final_scan_result, ) - def log_tool_start(self, agent_id: str, tool_name: str) -> int: + def log_tool_start( + self, + agent_id: str, + tool_name: str, + args: dict[str, Any] | None = None, + ) -> int: """Record a tool invocation in flight. Returns an exec_id.""" exec_id = self._next_exec_id self._next_exec_id += 1 self.tool_executions[exec_id] = { "agent_id": agent_id, "tool_name": tool_name, + "args": args or {}, "status": "running", "result": None, "timestamp": datetime.now(UTC).isoformat(),