An agent that creates a fresh ExecutionContext for every run() loses the previous conversation when that run ends. A Session gives one conversation a longer-lived container. Reusing its session_id lets later runs access earlier events, so a follow-up question can refer to something the user already said. A new or omitted session_id leaves the runs independent. section 6.1.2 section 6.3.4
A Session has a unique session_id, an optional user_id, accumulated events, an arbitrary state dictionary, and creation and update timestamps. Events hold conversation and execution history; state can hold progress on an interrupted task. An ExecutionContext belongs to one run, while the Session persists across runs: the agent restores session data at the start and saves it afterward. section 6.3.1 section 6.1.2
Listing 6.8 Session class design
class Session(BaseModel):
"""Container for persistent conversation state across multiple run() calls."""
session_id: str
user_id: str | None = None
events: list[Event] = Field(default_factory=list)
state: dict[str, Any] = Field(default_factory=dict)
created_at: datetime = Field(default_factory=datetime.now)
updated_at: datetime = Field(default_factory=datetime.now)
The Session class makes the persistent container concrete: events and state have separate fields, and both start empty. section 6.3.1
To connect the container to an agent:
- Use a SessionManager to create, retrieve, and save sessions by session_id. An in-memory implementation works for testing or a single process but loses its sessions when the program terminates; persistent storage needs another implementation. section 6.3.2
- Give ExecutionContext a Session reference and expose session.events and session.state through its events and state properties. Existing code can keep using context.events and context.state. section 6.3.3
- When run() receives a session_id, get or create that Session, attach it to the new context, then save it when execution completes. section 6.3.3
A follow-up across runs
Call await agent.run("My name is Alice", session_id="session_1"), then await agent.run("What's my name?", session_id="session_1"). The second run can answer “Alice” because its context reads the first run’s events from the shared Session. Without that shared identifier, it has no previous conversation to consult. section 6.3 section 6.3.4
State persistence also supports asynchronous human approval. Before a tool call requiring confirmation, the agent can save pending-call data in session.state and return; when a decision arrives, it restores that data and resumes. Convert pending Pydantic models to serializable dictionaries before storing them for JSON or database persistence, then validate them when restoring. section 6.3 section 6.3.5
A Session is short-term memory for one conversation, not knowledge automatically shared with every future conversation. Long-term memory selectively extracts information for use across session boundaries. Accumulated session history can also increase the context sent to the model, making context reduction relevant across runs. section 6.1.2 section 6.1.3
