Skip to main content
The Event System provides an immutable, type-safe event framework that drives agent execution and state management. Events form an append-only log that serves as both the agent’s memory and the integration point for auxiliary services. Source: openhands-sdk/openhands/sdk/event/

Core Responsibilities

The Event System has four primary responsibilities:
  1. Type Safety - Enforce event schemas through Pydantic models
  2. LLM Integration - Convert events to/from LLM message formats
  3. Append-Only Log - Maintain immutable event history
  4. Service Integration - Enable observers to react to event streams

Architecture

Key Components

Event Types

LLM-Convertible Events

Events that participate in agent reasoning and can be converted to LLM messages: The event system bridges agent events to LLM messages: Special Handling - Parallel Function Calling: When multiple ActionEvents share the same llm_response_id (parallel function calling):
  1. Group all ActionEvents by llm_response_id
  2. Combine into single Message with multiple tool_calls
  3. Only first event’s thought, reasoning_content, and thinking_blocks are included
  4. All subsequent events in the batch have empty thought fields
Example:

Internal Events

Events for metadata, control flow, and user actions (not sent to LLM): Source Types:
  • user: Event originated from user input
  • agent: Event generated by agent logic
  • environment: Event from system/framework/tools

Component Relationships

How Events Integrate

source vs LLM role

Events often carry two different concepts that are easy to confuse:
  • Event.source: where the event originated (user, agent, or environment). This is about attribution.
  • LLM role (e.g. Message.role / MessageEvent.llm_message.role): how the event should be represented to the LLM (system, user, assistant, tool). This is about LLM formatting.
These fields are intentionally independent. Common examples include:
  • Observations: tool results are typically source="environment" and represented to the LLM with role="tool".
  • Synthetic framework messages: the SDK may inject feedback or control messages (e.g. from hooks) as source="environment" while still using an LLM role="user" so the agent reads it as a user-facing instruction.
Do not infer event origin from LLM role. If you need to distinguish real user input from synthetic/framework messages, rely on Event.source (and any explicit metadata fields on the event), not the LLM role. Relationship Characteristics:
  • Agent → Events: Reads history for context, writes actions/messages
  • Conversation → Events: Owns and persists event log
  • Tools → Events: Create ObservationEvents after execution
  • Services → Events: Read-only observers for monitoring, visualization

Error Events: Agent vs Conversation

Two distinct error events exist in the SDK, with different purpose and visibility:

See Also