openhands-sdk/openhands/sdk/event/
Core Responsibilities
The Event System has four primary responsibilities:- Type Safety - Enforce event schemas through Pydantic models
- LLM Integration - Convert events to/from LLM message formats
- Append-Only Log - Maintain immutable event history
- 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):
- Group all ActionEvents by
llm_response_id - Combine into single Message with multiple
tool_calls - Only first event’s
thought,reasoning_content, andthinking_blocksare included - All subsequent events in the batch have empty thought fields
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, orenvironment). 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.
- Observations: tool results are typically
source="environment"and represented to the LLM withrole="tool". - Synthetic framework messages: the SDK may inject feedback or control messages (e.g. from hooks) as
source="environment"while still using an LLMrole="user"so the agent reads it as a user-facing instruction.
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:-
AgentErrorEvent
- Type: ObservationBaseEvent (LLM-convertible)
- Scope: Error for a specific tool call (has tool_name and tool_call_id)
- Source: “agent”
- LLM visibility: Sent as a tool message so the model can react/recover
- Effect: Conversation continues; not a terminal state
- Code: https://github.com/OpenHands/software-agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/llm_convertible/observation.py
-
ConversationErrorEvent
- Type: Event (not LLM-convertible)
- Scope: Conversation-level runtime failure (no tool_name/tool_call_id)
- Source: typically “environment”
- LLM visibility: Not sent to the model
- Effect: Run loop transitions to ERROR and run() raises ConversationRunError; surface top-level error to client applications
- Code: https://github.com/OpenHands/software-agent-sdk/blob/main/openhands-sdk/openhands/sdk/event/conversation_error.py
See Also
- Agent Architecture - How agents read and write events
- Conversation Architecture - Event log management
- Tool System - ActionEvent and ObservationEvent generation
- Condenser - Event history compression

