From 12539fbd925600b6939bd727496838fd3bd62c47 Mon Sep 17 00:00:00 2001 From: pavsoss Date: Sun, 12 Jul 2026 17:12:04 +0530 Subject: [PATCH 1/2] fix(core): route safety approval prompts through logging or renderer --- agentwatch/cli/main.py | 24 +++++++++++++++++++- agentwatch/core/safety.py | 46 ++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 18 deletions(-) diff --git a/agentwatch/cli/main.py b/agentwatch/cli/main.py index fb594fc5..7cb7d396 100644 --- a/agentwatch/cli/main.py +++ b/agentwatch/cli/main.py @@ -360,7 +360,29 @@ async def _run() -> None: require_approval_on_medium=True, ) safety = SafetyEngine(policy=p) - safety.set_approval_callback(cli_approval_handler) + + def cli_renderer(event, safety): + tool = event.tool_call + print("\n" + "=" * 60) + print("⚠️ AGENTWATCH SAFETY CHECK") + print("=" * 60) + print(f"Agent: {event.agent_name or event.agent_id}") + print(f"Action: {tool.tool_name if tool else 'unknown'}") + if tool and tool.raw_command: + print(f"Command: {tool.raw_command}") + if tool and tool.affected_resources: + print(f"Resources: {', '.join(tool.affected_resources)}") + print(f"Risk Level: {safety.risk_level.value.upper()}") + print(f"Risk Score: {safety.risk_score:.2f}") + print("Reasons:") + for r in safety.reasons: + print(f" • {r}") + print("=" * 60) + + async def wrapped_handler(event, check_safety): + return await cli_approval_handler(event, check_safety, renderer=cli_renderer) + + safety.set_approval_callback(wrapped_handler) adapter = ClaudeCodeAdapter(safety_engine=safety) diff --git a/agentwatch/core/safety.py b/agentwatch/core/safety.py index ffb0a1df..9c4116dc 100644 --- a/agentwatch/core/safety.py +++ b/agentwatch/core/safety.py @@ -873,12 +873,17 @@ def policy(self) -> SafetyPolicy: # ───────────────────────────────────────────── -async def cli_approval_handler(event: AgentEvent, safety: SafetyCheckData) -> bool: +async def cli_approval_handler( + event: AgentEvent, + safety: SafetyCheckData, + renderer: Callable[[AgentEvent, SafetyCheckData], None] | None = None +) -> bool: """Prompt on the TTY to approve or deny a risky tool call. Args: event: Event containing the tool call under review. safety: Risk metadata to display to the operator. + renderer: Optional callback to render the prompt UI. Returns: True if the user confirms; False in non-interactive mode or on deny. @@ -886,24 +891,31 @@ async def cli_approval_handler(event: AgentEvent, safety: SafetyCheckData) -> bo import sys tool = event.tool_call - print("\n" + "=" * 60) - print("⚠️ AGENTWATCH SAFETY CHECK") - print("=" * 60) - print(f"Agent: {event.agent_name or event.agent_id}") - print(f"Action: {tool.tool_name if tool else 'unknown'}") - if tool and tool.raw_command: - print(f"Command: {tool.raw_command}") - if tool and tool.affected_resources: - print(f"Resources: {', '.join(tool.affected_resources)}") - print(f"Risk Level: {safety.risk_level.value.upper()}") - print(f"Risk Score: {safety.risk_score:.2f}") - print("Reasons:") - for r in safety.reasons: - print(f" • {r}") - print("=" * 60) + + if renderer: + renderer(event, safety) + else: + logger.info("\n" + "=" * 60) + logger.info("⚠️ AGENTWATCH SAFETY CHECK") + logger.info("=" * 60) + logger.info(f"Agent: {event.agent_name or event.agent_id}") + logger.info(f"Action: {tool.tool_name if tool else 'unknown'}") + if tool and tool.raw_command: + logger.info(f"Command: {tool.raw_command}") + if tool and tool.affected_resources: + logger.info(f"Resources: {', '.join(tool.affected_resources)}") + logger.info(f"Risk Level: {safety.risk_level.value.upper()}") + logger.info(f"Risk Score: {safety.risk_score:.2f}") + logger.info("Reasons:") + for r in safety.reasons: + logger.info(f" • {r}") + logger.info("=" * 60) if not sys.stdin.isatty(): - print("Non-interactive session detected. Blocking action.") + if renderer: + print("Non-interactive session detected. Blocking action.") + else: + logger.info("Non-interactive session detected. Blocking action.") return False response = await asyncio.to_thread(input, "Allow this action? [y/N]: ") From d3ae162547fa24094d8661144f72915ab625fbdf Mon Sep 17 00:00:00 2001 From: pavsoss Date: Sun, 12 Jul 2026 17:21:32 +0530 Subject: [PATCH 2/2] style: run ruff format --- agentwatch/core/safety.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agentwatch/core/safety.py b/agentwatch/core/safety.py index 9c4116dc..8fb60996 100644 --- a/agentwatch/core/safety.py +++ b/agentwatch/core/safety.py @@ -874,9 +874,9 @@ def policy(self) -> SafetyPolicy: async def cli_approval_handler( - event: AgentEvent, + event: AgentEvent, safety: SafetyCheckData, - renderer: Callable[[AgentEvent, SafetyCheckData], None] | None = None + renderer: Callable[[AgentEvent, SafetyCheckData], None] | None = None, ) -> bool: """Prompt on the TTY to approve or deny a risky tool call. @@ -891,7 +891,7 @@ async def cli_approval_handler( import sys tool = event.tool_call - + if renderer: renderer(event, safety) else: