Session bundles compaction, retry, persistence, turn tracking, and hooks into a single class. If you want more control — composing only the behaviors you need, or writing custom middleware — use the functional Runner / Middleware / Conversation API instead.
Runners and Middleware
ARunner is an async generator function with the same shape as agent.run(). A Middleware transforms one Runner into another. You compose them with apply():
apply() wraps outermost. The ordering above means: turn tracking brackets everything, compaction runs once before retries, retry wraps only the agent call, and persistence saves after a successful response.
Available Middleware
Conversation
Conversation is a thin stateful wrapper over a composed Runner. It manages messages (updating from done events) and provides the same toUIMessageStream() and toResponse() methods as Session for AI SDK 5 integration:
Stream Combinators
For lightweight event stream transforms, four curried combinators are available:Writing Custom Middleware
A middleware is a function that takes a Runner and returns a Runner:Composing with pipe
For reusable middleware stacks, use pipe() to create a combined middleware: