Skip to content

Feature Request: Tool execution hook events (tool:before, tool:after) #7597

@er587

Description

@er587

Feature Request: Tool Execution Hook Events

Repository: https://github.com/openclaw/openclaw
Date: 2026-02-02
Author: Eric (@er587)


Summary

Add tool:before and tool:after hook events to enable audit logging, guardrails, and monitoring of tool executions — especially the exec tool which runs shell commands.

Motivation

Currently, OpenClaw hooks can listen to command events (/new, /reset, /stop) and lifecycle events (gateway:startup, agent:bootstrap), but there's no way to hook into tool executions.

This is a security gap for users who want to:

  1. Audit logging — Log all shell commands executed by the AI for compliance, debugging, or forensics
  2. Guardrails — Block or require approval for dangerous commands before execution
  3. Monitoring — Alert on suspicious patterns (e.g., rm -rf, curl | bash, credential access)
  4. Rate limiting — Prevent runaway tool calls

Current Workarounds

Approach Limitation
Parse session transcripts Post-hoc only, not real-time
Debug logs Show tool=exec but not the command string
tool_result_persist plugin Synchronous, meant for transform not logging, after-the-fact

None of these enable real-time interception or logging of tool calls.

Proposed Solution

New Hook Events

tool:before   — Fires before a tool executes
tool:after    — Fires after a tool completes

Or more granular:

tool:exec:before   — Before exec tool specifically
tool:exec:after    — After exec tool
tool:read:before   — Before file read
tool:write:before  — Before file write
tool:message:before — Before sending messages

Event Payload

interface ToolEvent {
  type: 'tool';
  action: 'before' | 'after';
  tool: string;           // 'exec', 'read', 'write', 'message', etc.
  toolCallId: string;
  sessionKey: string;
  timestamp: Date;
  
  // Tool-specific payload
  arguments: {
    command?: string;     // For exec
    path?: string;        // For read/write
    // ... other tool args
  };
  
  // For 'after' events
  result?: {
    success: boolean;
    output?: string;
    error?: string;
    durationMs: number;
  };
  
  // For guardrails (before events only)
  abort?: () => void;     // Call to prevent execution
  messages: string[];     // Push messages to user
}

Example Use Cases

1. Exec Audit Logger

const handler: HookHandler = async (event) => {
  if (event.type !== 'tool' || event.tool !== 'exec') return;
  
  const logEntry = {
    timestamp: event.timestamp.toISOString(),
    sessionKey: event.sessionKey,
    command: event.arguments.command,
    phase: event.action,
    ...(event.action === 'after' && {
      success: event.result?.success,
      durationMs: event.result?.durationMs,
    }),
  };
  
  await fs.appendFile(
    '~/.openclaw/logs/exec-audit.log',
    JSON.stringify(logEntry) + '\n'
  );
};

2. Dangerous Command Guardrail

const DANGEROUS_PATTERNS = [
  /rm\s+(-rf?|--recursive)/i,
  /curl.*\|\s*(bash|sh)/i,
  />\s*\/etc\//,
  /chmod\s+777/,
];

const handler: HookHandler = async (event) => {
  if (event.type !== 'tool' || event.tool !== 'exec' || event.action !== 'before') return;
  
  const cmd = event.arguments.command || '';
  
  for (const pattern of DANGEROUS_PATTERNS) {
    if (pattern.test(cmd)) {
      event.abort?.();
      event.messages.push(`⚠️ Blocked dangerous command: ${cmd.slice(0, 50)}...`);
      console.warn(`[guardrail] Blocked: ${cmd}`);
      return;
    }
  }
};

3. Real-time SIEM Integration

const handler: HookHandler = async (event) => {
  if (event.type !== 'tool') return;
  
  // Forward to Security Onion / Splunk / etc.
  await fetch(process.env.SIEM_WEBHOOK, {
    method: 'POST',
    body: JSON.stringify({
      event_type: 'openclaw_tool_exec',
      tool: event.tool,
      command: event.arguments.command,
      session: event.sessionKey,
      timestamp: event.timestamp.toISOString(),
    }),
  });
};

Implementation Notes

The infrastructure already exists:

  • Debug logs already capture tool=exec with toolCallId and runId
  • Session transcripts already store full tool call details
  • The hook system already supports async handlers with event context

The main work would be:

  1. Emit events from the tool execution path (likely in agent/embedded or tool dispatcher)
  2. Add event types to the hook type definitions
  3. Document the new events

Alternatives Considered

  1. Plugin tool_result_persist hook — Only fires after, synchronous, meant for data transformation not logging
  2. Middleware pattern — More invasive, hooks are cleaner
  3. External log parsing — Not real-time, misses blocked commands

Priority

High for security-conscious deployments. This enables:

  • SOC 2 / compliance audit trails
  • Defense-in-depth for self-hosted AI
  • Prompt injection mitigation (block suspicious commands)

Would be happy to contribute a PR if the maintainers agree on the approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions