Skip to content

fix(core): route safety approval prompts through logging or renderer#613

Open
pavsoss wants to merge 2 commits into
sreerevanth:mainfrom
pavsoss:issue-595-safety-print
Open

fix(core): route safety approval prompts through logging or renderer#613
pavsoss wants to merge 2 commits into
sreerevanth:mainfrom
pavsoss:issue-595-safety-print

Conversation

@pavsoss

@pavsoss pavsoss commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Closes #595

This PR addresses the issue of core/safety.py unconditionally writing approval prompts to stdout from a library path.

  • Added a Seam: Modified cli_approval_handler to accept an optional renderer callback.
  • Library-friendly Fallback: If no renderer is provided by an embedder, it defaults to using logger.info(), preventing stdout corruption for structured consumers.
  • Preserved CLI UX: Updated agentwatch/cli/main.py to pass in a cli_renderer using print(), ensuring the CLI keeps its exact same "pretty box" styling without any behavioral regressions.

Summary by CodeRabbit

  • New Features

    • Added a clearer safety review display for watched actions, including agent details, action or tool information, affected resources, risk level, score, and reasons.
    • Improved safety prompts and blocking messages across interactive and non-interactive sessions.
  • Bug Fixes

    • Standardized safety approval output for more consistent and understandable action reviews.

@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

@pavsoss is attempting to deploy a commit to the sreerevanth's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The safety approval handler now supports optional prompt rendering and logs its default output. The watch CLI supplies a structured safety-check renderer through a wrapped approval callback.

Changes

Safety approval rendering

Layer / File(s) Summary
Configurable approval rendering
agentwatch/core/safety.py
cli_approval_handler accepts an optional renderer, preserves approval handling, and routes default prompt output through logging.
Watch CLI safety display
agentwatch/cli/main.py
The watch command supplies a structured safety-check renderer and registers it through a wrapped approval callback.

Estimated code review effort: 2 (Simple) | ~15 minutes

Suggested labels: ready-to-merge, Medium, SSoC26

Suggested reviewers: sreerevanth

Poem

A rabbit checks the tools with care,
Then paints the risks in tidy air.
Logs replace the noisy cheer,
The CLI makes the prompt clear.
Hop approved—or blocked right here!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes routing safety approval prompts through logging or a renderer.
Linked Issues check ✅ Passed The PR adds a renderer seam, preserves CLI prompt rendering, and keeps fail-closed approval behavior unchanged for #595.
Out of Scope Changes check ✅ Passed The changes stay focused on safety prompt rendering and logging, with no unrelated functionality added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Results

Check Result
Tests (pytest tests/) ✅ success
Lint (ruff check .) ❌ failure
Coverage (agentwatch) 74.06%

Python 3.12 · commit d3ae162

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
agentwatch/cli/main.py (1)

364-380: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the safety-check display format into a shared helper to avoid duplication.

cli_renderer reproduces the exact same line-by-line format as the logger.info fallback in safety.py (lines 898–912). If the format changes in one location, the other can silently drift. Consider extracting a content generator in safety.py that returns the display lines, then have each path handle only the output mechanism.

♻️ Proposed refactor: shared content generator

In agentwatch/core/safety.py:

def _safety_check_lines(event: AgentEvent, safety: SafetyCheckData) -> list[str]:
    """Return the formatted safety-check display lines."""
    tool = event.tool_call
    lines = [
        "\n" + "=" * 60,
        "⚠️  AGENTWATCH SAFETY CHECK",
        "=" * 60,
        f"Agent:      {event.agent_name or event.agent_id}",
        f"Action:     {tool.tool_name if tool else 'unknown'}",
    ]
    if tool and tool.raw_command:
        lines.append(f"Command:    {tool.raw_command}")
    if tool and tool.affected_resources:
        lines.append(f"Resources:  {', '.join(tool.affected_resources)}")
    lines.append(f"Risk Level: {safety.risk_level.value.upper()}")
    lines.append(f"Risk Score: {safety.risk_score:.2f}")
    lines.append("Reasons:")
    lines.extend(f"  • {r}" for r in safety.reasons)
    lines.append("=" * 60)
    return lines

Then the logger fallback in cli_approval_handler becomes:

     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)
+        for line in _safety_check_lines(event, safety):
+            logger.info(line)

And cli_renderer in main.py becomes:

-            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)
+            def cli_renderer(event, safety):
+                for line in _safety_check_lines(event, safety):
+                    print(line)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@agentwatch/cli/main.py` around lines 364 - 380, Extract the duplicated
safety-check formatting from cli_renderer and the logger fallback in
cli_approval_handler into a shared _safety_check_lines helper in safety.py. Have
the helper return the complete ordered display lines, preserving the existing
conditional tool fields, risk details, reasons, and separators; update both
callers to only handle their respective output mechanisms.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@agentwatch/cli/main.py`:
- Around line 364-380: Extract the duplicated safety-check formatting from
cli_renderer and the logger fallback in cli_approval_handler into a shared
_safety_check_lines helper in safety.py. Have the helper return the complete
ordered display lines, preserving the existing conditional tool fields, risk
details, reasons, and separators; update both callers to only handle their
respective output mechanisms.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d80c6637-2248-459b-a3be-9b488bb5cc3a

📥 Commits

Reviewing files that changed from the base of the PR and between a492da5 and d3ae162.

📒 Files selected for processing (2)
  • agentwatch/cli/main.py
  • agentwatch/core/safety.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] core/safety.py writes the approval prompt to stdout from a library path

1 participant