Agent Event Protocol

Agent Event Protocol (AEP) is an open draft standard for normalizing AI coding agent activity and hook control responses.

One event language for many agents

Every coding agent emits hook payloads with slightly different shapes. Some send a model id. Some send a working directory. Some split tool input, command text, MCP server details, previews, and full output across several fields. Tools that consume those events end up with per-agent special cases that calcify quickly.

AEP defines one canonical envelope and a small event vocabulary, plus a standard hook response for control decisions. New agents plug in by emitting AEP directly, or by writing one declarative mapping file.

The public draft is available for review on GitHub. The current review draft is 0.1.

What AEP makes explicit

Most coding agents already expose a basic pre-tool hook. AEP is about the activity model around that hook: stable event identity, action correlation, catalog identity for tools and MCP servers, session and workspace context, execution outcomes, and control responses. Those are the pieces consumers need for approvals, audit trails, policy evaluation, and observability as agents move beyond one tool call at a time.

Choose a provider to see how its current hook surface maps into the same canonical needs. The AEP column is the shape consumers can target; the provider column shows where that data comes from today or where the source is still implicit. Top-level event fields stay top-level in the spec: no wrapper object is implied.

NeedAEP shape
Event identity

A consumer needs a stable event id, a canonical event type, and a timestamp before vendor-specific hook details.

id
type
time
missing
PreToolUse
timestamp
Agent identity

Events need a stable producer identity so dashboards and policy rules can distinguish runtimes without guessing from hook names.

agent.slug
agent.display_name
agent.version
agent
agent_name
missing
Session and workspace

Policy and observability need to group activity by session, conversation, user, repository, and current directory.

session.id
session.conversation_id
session.turn_id
user.email
workspace.cwd
workspace.roots
session_id
conversation_id
missing
missing
cwd
missing
Correlation

Approvals, denials, completions, failures, and audit records need to join back to the same action.

action.id
response.request_event_id
response.action_id
tool_use_id
missing
tool_use_id
Invocation

A tool call, skill use, subagent spawn, or future action should have one place for its kind and arguments.

action.type
action.input
tool.name
inferred: tool_call
tool_input
tool_name
Catalog identity

Policy often cares whether a call is native, MCP, connector, extension, or custom, and which server supplied the tool.

tool.id
tool.type
tool.schema
server.name
server.url
missing
inferred from tool_name
missing
mcp__<server>__<tool>
missing
Control response

Hook consumers need a standard way to allow, deny, ask, defer, block, or add context while preserving the native response shape.

control.behavior
control.message
control.updated_input
control.additional_context
decision.reason
permissionDecision
reason
updatedInput
additionalContext
reason
Execution result

Once an action runs, consumers need the final status, output, error, and execution duration without rewriting per-agent parsers.

action.status
action.output
action.error
metrics.duration_ms
status
tool_response
error
missing
Beyond tools

The protocol has to scale from tool approval into prompts, model activity, subagents, compaction, notifications, and workspace changes.

prompt.submitted
model.thought
context.compacted
subagent.start
session.end
UserPromptSubmit
missing
PreCompact
SubagentStart
SessionEnd

The basic tool request stays simple: a provider can emit action.requestedwith action.input and tool.name. The same envelope can then carry richer activity without adding a new one-off schema for each agent capability.

At a glance

The full canonical envelope. aep_version, id, type, time, and optional hook are top-level fields. Catalog nouns (tool, server, skill) are top-level peers; the action object is the verb that references them via discriminators.

Example AEP event

Snake_case JSON. Producers omit fields they don't provide rather than emitting null. Consumers tolerate omitted fields and ignore unknown extensions.x-* keys.

{
  "aep_version": "0.1",
  "id": "evt_01HXY7GZ3K8MZ9",
  "type": "action.requested",
  "time": "2026-05-09T12:00:00.000Z",
  "hook": "PreToolUse",

  "agent": {
    "slug": "claude",
    "display_name": "Claude Code",
    "version": "1.4.2"
  },
  "session": {
    "id": "sess_abc",
    "conversation_id": "conv_8a1",
    "permission_mode": "default"
  },
  "user": { "email": "user@example.com" },
  "workspace": { "cwd": "/workspace/project" },
  "model": { "id": "claude-sonnet-4-6", "provider": "anthropic" },

  "action": {
    "type": "tool_call",
    "id": "call_xyz",
    "input": { "command": "bun test" }
  },
  "tool": {
    "id": "01HXY7QWPZ6PR0XEHZ3M2Q3WYZ",
    "type": "native",
    "name": "Bash",
    "description": "Executes a given bash command and returns its output. The working directory persists between commands, but shell state does not.",
    "version": "1.4.2",
    "schema": {
      "type": "object",
      "required": ["command"],
      "properties": {
        "command":           { "type": "string",  "description": "The command to execute" },
        "description":       { "type": "string",  "description": "5-10 word description of what the command does" },
        "timeout":           { "type": "number",  "description": "Optional timeout in milliseconds (max 600000, default 120000)" },
        "run_in_background": { "type": "boolean", "description": "Run command in background; use Read/Monitor for output" }
      }
    }
  }
}

Objects in detail

Every group in the envelope, expanded. All fields documented here are optional unless noted; unset fields SHOULD be omitted from the wire.

Event types

Twenty-one canonical types organized into five families. Past-participle for actions that completed (action.completed, prompt.submitted); bare noun for entities (model.thought, session.start).

Required fields below are in addition to the core envelope:aep_version, id, type, time, and agent. Parenthetical chips are conditional on the action kind or tool type.

Lifecycle

5 types

Session boundaries, agent process lifecycle, errors.

Prompts & responses

5 types

User prompts, model thoughts, responses, streaming deltas, plans.

Actions

4 types

Tool, skill, and subagent invocation lifecycle. action.type discriminates the kind.

Orchestration

4 types

Subagent lifecycle, context compaction, workspace state mutations.

Notifications

3 types

Agent notifications and interactive question/answer pairs.

Hook responses

Blocking hooks have two sides: an immutable AEP event sent into the hook, and a separate AEP response returned to the agent runtime. Apps can join both records by action.id to show one activity row.

{
  "aep_version": "0.1",
  "id": "rsp_01HXY7GZ3K8MZ9",
  "request_event_id": "evt_01HXY7GZ3K8MZ9",
  "action_id": "call_xyz",
  "time": "2026-05-09T12:00:04.820Z",
  "control": {
    "behavior": "allow",
    "message": "Matched allowlist rule shell.tests",
    "updated_input": { "command": "bun test --filter api" }
  },
  "decision": {
    "outcome": "approved",
    "by": "auto-policy",
    "reason": "matched allowlist rule shell.tests",
    "response_time_ms": 4820
  }
}
BehaviorMeaning
allowApprove or continue the requested action.
denyReject the requested action and return feedback when supported.
askEscalate to a human/user confirmation flow.
deferPause gracefully so the action can be resumed later.
blockBlock the current result/stop and force another agent turn.
continueExplicitly allow the lifecycle to continue.
stopStop the agent or interrupt the run when supported.
abstainReturn no opinion and let the provider default behavior run.

Approved flow

action.requested → response allow / approved action.completed or action.failed.

Denied flow

action.requested → response deny / denied. action.denied may also be emitted for an audit stream.

Display styles

Producer hints for how a content/value field is intended to render. Consumers may ignore them and substitute their own.

StyleUse for
plain_textShort strings, ids, names, and compact values.
markdownPrompts, thoughts, responses, plans, readable tool output.
indented_jsonStructured action input/output, server payloads, errors.
key_valueDynamic object fields when full JSON would hide useful detail.
pathWorking directories, project paths, file paths, config paths.
urlExternal references, repositories, websites, MCP server URLs.
imageImage attachments (screenshots, diagrams, charts).
videoVideo attachments (screen recordings, demos).
audioAudio attachments (voice clips, dictation).
badgeStatus, decision, type, category.
durationMilliseconds shown as readable durations.
timestampEpoch or ISO timestamps shown in local time.

Integrate your agent

Two paths, same wire format. Pick native AEP if you can change your hook output; pick a mapping if you ship an existing agent and don't want to.

Native AEP

Emit AEP envelopes directly from your hooks or SDK. Recommended for new agents. Consumers need zero per-agent code.

Mapping file

Keep your existing hook format. Write one YAML file describing how each of your events maps to AEP. The consumer translates at ingestion.

Mapping files

A mapping file says: for each of my hook events, this is the AEP type and these are the JSON Pointer paths to the source fields. Declarative, reviewable, swappable without a consumer rebuild.

schema_version: aep.mapping/v1
agent: cursor
display_name: Cursor

events:
  - id: cursor.shell_approval
    source_event: beforeShellExecution
    when:
      /event_name: beforeShellExecution
    canonical_event: action.requested
    fields:
      action.id:
        source: /tool_call_id
      action.input:
        source: /tool_input
        display_style: indented_json
      tool.name:
        source: /tool_name
        display_style: plain_text
      workspace.cwd:
        source: /cwd
        display_style: path
    content:
      - type: prompt
        text:
          source: /prompt
        style: markdown

The mapping format is part of the open standard. Draft provider mappings are available in the public repo for review; every consumer can write or adapt its own.

Versioning and conformance

AEP uses semver. The public review draft is 0.1; producers emit aep_version: "0.1". Breaking changes are still possible before 1.0, so consumers should pin to the draft version they implement.

The formal spec, examples, changelog, and draft provider mappings live in the Agent Event Protocol GitHub repo. Issues, discussions, and pull requests are open for review feedback.

A reference JSON Schema and conformance fixtures will ship alongside 1.0 final. The smoke test until then: a consumer that knows nothing about your agent should be able to render at least the title (from type and agent.slug) and tool name without per-agent code.

Agent Approve uses AEP to render every supported agent on iPhone and Apple Watch with one rendering layer. See the changelog for AEP draft updates and product changes.