Skip to content

Tool execution guardrails: confirmation popup before dangerous commands #438

@kovtcharov

Description

@kovtcharov

Summary

Add a confirmation mechanism so the Chat UI can ask the user for permission before the agent executes potentially dangerous tools (e.g., run_shell_command). The user should see the exact command, and can choose Allow, Deny, or Always Allow.

Motivation

Currently, when a user asks the agent a question like "what files are in this folder?", the agent calls run_shell_command with no user visibility or consent. This is a safety gap — shell commands can have side effects and users should be aware of what's running on their machine.

Design

Agent Framework (src/gaia/agents/base/)

  1. OutputHandler base class — Add a new method:

    def confirm_tool_execution(self, tool_name: str, tool_args: dict) -> bool:
        """Request confirmation before executing a tool. Returns True to proceed."""
        return True  # Default: auto-approve (backward compatible)
  2. agent.py — Before _execute_tool(), check if the tool requires confirmation:

    TOOLS_REQUIRING_CONFIRMATION = {"run_shell_command"}
    
    if tool_name in TOOLS_REQUIRING_CONFIRMATION:
        approved = self.console.confirm_tool_execution(tool_name, tool_args)
        if not approved:
            tool_result = {"status": "denied", "error": "User denied execution"}
            # skip _execute_tool, continue to next step
  3. AgentConsole (CLI) — Could prompt in terminal or auto-approve.

SSE Handler (src/gaia/chat/ui/sse_handler.py)

  1. SSEOutputHandler.confirm_tool_execution() — Emits a tool_confirm event and blocks on a threading.Event:
    def confirm_tool_execution(self, tool_name, tool_args):
        confirm_id = str(uuid4())
        self._confirm_event = threading.Event()
        self._confirm_result = None
        self._emit({
            "type": "tool_confirm",
            "confirm_id": confirm_id,
            "tool": tool_name,
            "args": tool_args,
            "command": tool_args.get("command", ""),
        })
        self._confirm_event.wait(timeout=60)  # 60s timeout
        return self._confirm_result == "allow"

Server (src/gaia/chat/ui/server.py)

  1. POST /api/chat/confirm endpoint:
    @app.post("/api/chat/confirm")
    async def confirm_tool(request: ConfirmRequest):
        # Sets the threading.Event on the SSE handler
        sse_handler.set_confirmation(request.confirm_id, request.decision)

Frontend (src/gaia/apps/chat/webui/)

  1. ToolConfirmModal component — Shows command details with Allow/Deny/Always Allow buttons
  2. api.tsconfirmToolExecution(confirmId, decision) function
  3. localStoragegaia-chat-allowed-commands stores the "Always Allow" list. Frontend auto-responds to tool_confirm events for matching commands without showing the popup.

Acceptance Criteria

  • run_shell_command triggers a confirmation popup in the Chat UI
  • Popup shows the exact command to be executed
  • "Allow" executes once, "Deny" skips, "Always Allow" persists
  • "Always Allow" list survives page reloads (localStorage)
  • CLI agent is unaffected (auto-approves by default)
  • 60-second timeout if user doesn't respond
  • Agent handles denial gracefully (continues to next step or answers)

Files to Modify

  • src/gaia/agents/base/console.pyOutputHandler.confirm_tool_execution()
  • src/gaia/agents/base/agent.py — Confirmation check before _execute_tool()
  • src/gaia/chat/ui/sse_handler.py — Blocking confirm with threading.Event
  • src/gaia/chat/ui/server.py/api/chat/confirm endpoint
  • src/gaia/chat/ui/models.pyConfirmRequest model
  • src/gaia/apps/chat/webui/src/components/ToolConfirmModal.tsx — New component
  • src/gaia/apps/chat/webui/src/services/api.tsconfirmToolExecution()
  • src/gaia/apps/chat/webui/src/components/ChatView.tsx — Handle tool_confirm events

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions