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

ComponentPurposeDesign
EventBase event classImmutable Pydantic model with ID, timestamp, source
LLMConvertibleEventLLM-compatible eventsAbstract class with to_llm_message() method
MessageEventText messagesUser or assistant conversational messages with skills
ActionEventTool callsAgent tool invocations with thought, reasoning, security risk
ObservationBaseEventTool response baseBase for all tool call responses
ObservationEventTool resultsSuccessful tool execution outcomes
UserRejectObservationUser rejectionUser rejected action in confirmation mode
AgentErrorEventAgent errorsErrors from agent/scaffold (not model output)
SystemPromptEventSystem contextSystem prompt with tool schemas
CondensationSummaryEventCondenser summaryLLM-convertible summary of forgotten events
ConversationStateUpdateEventState updatesKey-value conversation state changes
CondensationCondensation resultEvents being forgotten with optional summary
CondensationRequestRequest compressionTrigger for conversation history compression
PauseEventUser pauseUser requested pause of agent execution

Event Types

LLM-Convertible Events

Events that participate in agent reasoning and can be converted to LLM messages:
Event TypeSourceContentLLM Role
MessageEvent (user)userText, imagesuser
MessageEvent (agent)agentText reasoning, skillsassistant
ActionEventagentTool call with thought, reasoning, security riskassistant with tool_calls
ObservationEventenvironmentTool execution resulttool
UserRejectObservationenvironmentRejection reasontool
AgentErrorEventagentError detailstool
SystemPromptEventagentSystem prompt with tool schemassystem
CondensationSummaryEventenvironmentSummary of forgotten eventsuser
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:
ActionEvent(llm_response_id="abc123", thought="Let me check...", tool_call=tool1)
ActionEvent(llm_response_id="abc123", thought=[], tool_call=tool2)
→ Combined into single Message(role="assistant", content="Let me check...", tool_calls=[tool1, tool2])

Internal Events

Events for metadata, control flow, and user actions (not sent to LLM):
Event TypeSourcePurposeKey Fields
ConversationStateUpdateEventenvironmentState synchronizationkey (field name), value (serialized data)
CondensationRequestenvironmentTrigger history compressionSignal to condenser when context window exceeded
CondensationenvironmentCompression resultforgotten_event_ids, summary, summary_offset
PauseEventuserUser pause actionIndicates agent execution was paused by user
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