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
| Component | Purpose | Design |
|---|---|---|
Event | Base event class | Immutable Pydantic model with ID, timestamp, source |
LLMConvertibleEvent | LLM-compatible events | Abstract class with to_llm_message() method |
MessageEvent | Text messages | User or assistant conversational messages with skills |
ActionEvent | Tool calls | Agent tool invocations with thought, reasoning, security risk |
ObservationBaseEvent | Tool response base | Base for all tool call responses |
ObservationEvent | Tool results | Successful tool execution outcomes |
UserRejectObservation | User rejection | User rejected action in confirmation mode |
AgentErrorEvent | Agent errors | Errors from agent/scaffold (not model output) |
SystemPromptEvent | System context | System prompt with tool schemas |
CondensationSummaryEvent | Condenser summary | LLM-convertible summary of forgotten events |
ConversationStateUpdateEvent | State updates | Key-value conversation state changes |
Condensation | Condensation result | Events being forgotten with optional summary |
CondensationRequest | Request compression | Trigger for conversation history compression |
PauseEvent | User pause | User requested pause of agent execution |
Event Types
LLM-Convertible Events
Events that participate in agent reasoning and can be converted to LLM messages:| Event Type | Source | Content | LLM Role |
|---|---|---|---|
| MessageEvent (user) | user | Text, images | user |
| MessageEvent (agent) | agent | Text reasoning, skills | assistant |
| ActionEvent | agent | Tool call with thought, reasoning, security risk | assistant with tool_calls |
| ObservationEvent | environment | Tool execution result | tool |
| UserRejectObservation | environment | Rejection reason | tool |
| AgentErrorEvent | agent | Error details | tool |
| SystemPromptEvent | agent | System prompt with tool schemas | system |
| CondensationSummaryEvent | environment | Summary of forgotten events | user |
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):| Event Type | Source | Purpose | Key Fields |
|---|---|---|---|
| ConversationStateUpdateEvent | environment | State synchronization | key (field name), value (serialized data) |
| CondensationRequest | environment | Trigger history compression | Signal to condenser when context window exceeded |
| Condensation | environment | Compression result | forgotten_event_ids, summary, summary_offset |
Invariants (Normative)
Event Immutability
All events inherit fromEvent / LLMConvertibleEvent with Pydantic config frozen=True and extra="forbid".
Natural language invariant:
- Once appended to the event log, an event must be treated as immutable. Mutations are represented as new events, not edits.
context Event inv Frozen: self.model_config.frozen = true
LLM-Convertible Stream Can Be Reconstructed Deterministically
Natural language invariant:LLMConvertibleEvent.events_to_messages(events)must produce the exact LLM message stream used for decision making, including batching of parallel tool calls.
Parallel Tool Calls are Batched by llm_response_id
When multipleActionEvents share the same llm_response_id, they represent a single assistant turn with multiple tool calls.
Natural language invariant:
- In a batch, only the first
ActionEventmay containthought/reasoning; subsequent actions must have emptythought. This is asserted when combining events.
Condensation is a Pure View Transformation
Condensation.apply(events) removes forgotten events and optionally inserts a synthetic CondensationSummaryEvent at summary_offset.
Natural language invariants:
- Condensation never mutates existing events; it returns a new list.
forgotten_event_idsmust refer to events that exist in the input list (otherwise the operation is a no-op for those IDs).- If
summaryis present,summary_offsetmust also be present to insert the summary into the view; otherwise the summary is metadata only.
context Condensation inv SummaryOffsetPair: (self.summary <> null) implies (self.summary_offset <> null) or true -- insertion requires both; metadata-only summary allowed
- user: Event originated from user input
- agent: Event generated by agent logic
- environment: Event from system/framework/tools
Component Relationships
How Events Integrate
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

