Agent Hooks

Abstract

Agent Hooks is an open specification for structured lifecycle events emitted by AI-powered developer tools, agents, and assistants. It defines a vendor-neutral format for recording what an AI agent did, who initiated it, and why enabling cost transparency, security auditing, policy enforcement, and agent observability across any compliant tool.

1. Motivation

Modern AI coding tools such as Cursor, Windsurf Cascade, Claude Code, Google Gemini CLI, GitHub Copilot, and Codex operate as stateful agents, not stateless APIs. Each tool has independently introduced "hook" event systems that let developers intercept agent actions at key lifecycle points. The capabilities converge, but the naming, schemas, and feature sets diverge:

  • Cursor: beforeShellExecution, afterFileEdit, afterAgentResponse

  • Claude Code: PreToolUse, PostToolUse, SessionStart, PreCompact

  • Windsurf Cascade: PreRunCommand, PostWriteCode, PreMCPToolUse

  • Gemini CLI: BeforeTool, AfterAgent, PreCompress

  • GitHub Copilot: preToolUse, postToolUse, sessionStart

As a result:

  • Users cannot compare cost or execution behavior across tools

  • Platform vendors lack a shared accountability format

  • Third-party tooling (cost, security, compliance) requires custom integration per vendor

  • Token consumption are opaque or delayed

  • No shared vocabulary exists for "what just happened"

Agent Hooks defines a minimal but extensible event surface for AI-native workflows, providing the common language that is missing.

2. Goals

  1. Interoperability: Any compliant tool can emit and consume hook events in one format.

  2. Granularity: Support events from session lifecycle through individual tool invocations.

  3. Attribution: Every event carries identity (who), action (what), and rationale (why).

  4. Extensibility: Vendors can add custom metadata without breaking compatibility.

  5. Human & Machine Readable: Events are JSON and readable without special tooling.

3. Non-Goals

  1. Standardizing Prompts: Agent Hooks does not define prompt formats or agent logic.

  2. Replacing Internal Telemetry: Vendors may keep their own systems; Agent Hooks is the external contract.

  3. Prescribing Behavior: The spec standardizes signals, not how agents should act.

  4. Transport Protocol: Delivery mechanism (stdout, HTTP, gRPC, queues) is out of scope.

  5. UI Requirements: Agent Hooks does not require any specific interface.

4. Terminology

Term

Definition

Hook Event

A single immutable fact emitted by an AI agent during its lifecycle

Actor

The entity initiating or executing an action (human, AI agent, or system)

Session

A bounded interaction window (IDE open→close, CLI invocation, agent run)

Action

An external operation performed by the agent (shell, file I/O, tool call)

Mode

The agent mode the hook event was in when emitted

Model

The AI model used when a hook event was emitted

Compaction

Summarization or removal of conversation history to fit context limits

Actor Types

Type

Code

Description

Hook Event

user

A single immutable fact emitted by an AI agent during its lifecycle

AI Agent

agent

The AI assistant or coding agent

System

system

Platform infrastructure (e.g., auto-compaction, timeout)

Agent Modes

Mode

Description

agent

Autonomous multi-step execution

plan

Planning or strategy sub-agent

execute

Code-writing execution phase

chat

Conversational interaction

edit

Direct file editing context

5. Architecture Overview

Agent Hooks is a data specification, not a product. It defines how to record hook events. Storage and transport mechanisms are implementation-defined.

Each vendor maps its native events to the Agent Hooks schema. Consumers receive a unified stream regardless of which tool emitted the event.

6. Core Specification

6.1 Event Envelope Schema

Every hook event MUST include the following envelope:

6.1 Event Envelope Schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://agent-hooks.dev/schemas/v1/event.json",
  "title": "Agent Hooks Event",
  "type": "object",
  "required": ["spec_version", "event_id", "event_type", "timestamp", "source", "session_id"],  "properties": {
    "spec_version": {
      "type": "string",
      "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
      "description": "Agent Hooks specification version (e.g., '0.1.0')"
    },
    "event_id": {
      "type": "string",
      "format": "uuid",
      "description": "Unique identifier for this event"
    },
    "event_type": {
      "type": "string",
      "description": "Unified event type using dot-separated Category.Subcategory format"
    },
    "timestamp": {
      "type": "string",
      "format": "date-time",
      "description": "RFC 3339 timestamp when event occurred"
    },
    "source": {
      "$ref": "#/$defs/source",
      "description": "The tool that emitted this event"
    },
    "session_id": {
      "type": "string",
      "description": "Identifier tying events to the same session"
    },
    "parent_session_id": {
      "type": "string",
      "description": "Parent session ID for nested or sub-agent sessions"
    },
    "user": {
      "$ref": "#/$defs/user",
      "description": "The human who initiated the agent session"
    },
    "actor": {
      "$ref": "#/$defs/actor",
      "description": "The entity that triggered this specific event"
    },
    "agent_mode": {
      "type": "string",
      "enum": ["agent", "plan", "execute", "chat", "edit"],
      "description": "The mode the agent was operating in"    },
    "data": {
      "type": "object",
      "description": "Event-type-specific payload"
    },
    "risk": {
      "$ref": "#/$defs/risk",
      "description": "Risk assessment for this event"
    },
    "metadata": {
      "type": "object",
      "description": "Vendor-specific or implementation-specific data"
    }
  },
  "$defs": {
    "source": {
      "type": "object",
      "required": ["tool"],
      "properties": {
        "tool": {
          "type": "string",
          "description": "Tool name (e.g., 'cursor', 'claude-code', 'gemini-cli', 'copilot', 'windsurf')"
        },
        "version": {
          "type": "string",
          "description": "Tool version"
        }
      }
    },
    "user": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "User identifier (email, username, or UUID)"
        },
        "name": {
          "type": "string"
        }
      }
    },
    "actor": {
      "type": "object",
      "required": ["type"],      "properties": {
        "type": {
          "type": "string",
          "enum": ["user", "ai_agent", "system"]
        },
        "id": {
          "type": "string",
          "description": "Actor identifier"
        },
        "name": {
          "type": "string"
        }
      }
    },
    "risk": {
      "type": "object",
      "properties": {
        "score": {
          "type": "integer",
          "minimum": 0,
          "maximum": 100,
          "description": "Numeric risk score (0 = no risk, 100 = critical)"
        },
        "level": {
          "type": "string",
          "enum": ["low", "medium", "high", "critical"],
          "description": "Categorical risk label"
        },
        "factors": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Tags explaining why the risk was scored (e.g., 'SECRET_PATTERN', 'DESTRUCTIVE_OP')"
        }
      }
    },
    "action": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "description": "Standardized action name (e.g., 'shell', 'read_file', 'write_file', 'mcp:<tool>')"
        },
        "input": {          "type": "object",
          "description": "Structured parameters for the action"
        },
        "result": {
          "type": "object",
          "properties": {
            "success": { "type": "boolean" },
            "exit_code": { "type": "integer" },
            "error_message": { "type": "string" },
            "output": { "type": "string", "description": "Truncated or summarized output" }
          }
        }
      }
    },
    "decision": {
      "type": "object",
      "properties": {
        "outcome": {
          "type": "string",
          "enum": ["allow", "deny", "ask"],
          "description": "Hook decision outcome"
        },
        "reason": {
          "type": "string",
          "description": "Human-readable reason for the decision"
        }
      }
    },
    "context": {
      "type": "object",
      "properties": {
        "trigger": {
          "type": "string",
          "enum": ["auto", "manual", "system"],
          "description": "What triggered the context operation"
        },
        "tokens_used": {
          "type": "integer",
          "description": "Current token count in use"
        },
        "tokens_limit": {
          "type": "integer",
          "description": "Maximum token limit before compaction triggers"
        },        "percentage_used": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "description": "Percentage of context window consumed (tokens_used / tokens_limit * 100)"
        },
        "tokens_before": {
          "type": "integer",
          "description": "Token count before compaction"
        },
        "tokens_after": {
          "type": "integer",
          "description": "Token count after compaction"
        },
        "tokens_removed": {
          "type": "integer"
        }
      }
    },
    "model": {
      "type": "object",
      "properties": {
        "model_id": {
          "type": "string",
          "description": "Model identifier following provider/model convention (e.g., 'anthropic/claude-sonnet-4-20250514')"
        },
        "input_tokens": { "type": "integer" },
        "output_tokens": { "type": "integer" },
        "latency_ms": { "type": "integer" },
        "retry_count": { "type": "integer" }
      }
    }
  }
}
6.2 Minimal Valid Event
{
  "spec_version": "0.1.0",
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_type": "Session.Start",
  "timestamp": "2026-02-08T19:22:11.123Z",
  "source": {
    "tool": "cursor",
    "version": "2.3.41"
  },
  "session_id": "sess_01HX"
}

6.3 Event Naming Convention

Event types use a dot-separated Category.Subcategory format. This avoids the fragmentation where beforeShellExecution (Cursor), PreRunCommand (Windsurf), preToolUse (Copilot), PreToolUse (Claude), and BeforeTool (Gemini) all describe the same concept.

Pattern

Meaning

Session.*

Session lifecycle

Prompt.*

User input events

Agent.*

Agent reasoning, planning, responses

Action.*

Tool/command execution

Model.*

LLM inference calls

Context.*

Context management (compaction)

Retry.*

Retry and loop detection

Policy.*

Safety and policy enforcement

©2026 Asante Babers