How Session Isolation Works in Agno's AgentOS for Multi-User Environments
Agno's AgentOS guarantees session isolation by binding every execution to a unique RunContext containing a session_id and session_state dictionary, ensuring that concurrent users operate in separate namespaces while allowing steps within the same run to share mutable state.
Session isolation is a critical security and data integrity feature for any multi-user agent framework. In the agno-agi/agno repository, the AgentOS architecture implements robust isolation mechanisms that prevent cross-talk between concurrent users while maintaining state continuity within individual workflows. This implementation centers on the RunContext class and persistent storage abstractions that together create isolated execution environments.
The RunContext Foundation for Session Isolation
At the core of Agno's isolation strategy is the RunContext class defined in libs/agno/agno/run/base.py. Every workflow execution receives a fresh RunContext instance that carries two critical identifiers: a unique session_id string and a mutable session_state dictionary. When an HTTP request or external trigger initiates a workflow, the OS instantiates this context with the caller's specific session identifier.
All steps, tools, and sub-workflows receive the same RunContext instance during execution. This design ensures that operations read from and write to the same session_state only for that specific session, effectively creating an isolated namespace per user request.
from agno.run.base import RunContext
# A fresh request from user "alice" gets its own session ID
run_ctx = RunContext(
run_id="run-001",
session_id="alice-session", # unique per user / request
user_id="alice", # optional user identifier
session_state={}, # start with empty state
)
# Pass the context into a workflow
workflow = MyWorkflow()
output = workflow.run(run_ctx)
Intra-Run State Sharing vs. Inter-Run Isolation
The system distinguishes between two isolation boundaries: sharing state within a single execution versus complete separation across different sessions.
Parallel Step Execution Within a Session
When a workflow executes parallel steps within the same RunContext, those steps share the same session_state object through a shallow copy mechanism. This allows sibling steps to see each other's updates in real-time during the execution, facilitating coordination between concurrent operations inside a single user session.
According to the test suite in libs/agno/tests/unit/workflow/test_parallel_run_context_isolation.py, parallel steps receive identical object references for session_state, confirming that mutations are immediately visible across all branches:
# In test_parallel_run_context_isolation.py
shared_state = {"shared": True}
run_context = RunContext(run_id="test", session_id="test", session_state=shared_state)
# Two parallel steps receive the *same* `run_context`
a_state_id = id(run_context.session_state) # → same object
b_state_id = id(run_context.session_state) # → same object
assert a_state_id == b_state_id
Steps can mutate this shared state during execution, as shown in this counter implementation:
from agno.step import Step
class CounterStep(Step):
async def aexecute(self, run_context: RunContext) -> dict:
# Increment a counter stored in the session
state = run_context.session_state or {}
state["counter"] = state.get("counter", 0) + 1
run_context.session_state = state
return {"counter": state["counter"]}
Cross-Session Boundaries
While parallel steps share state within a run, the session_id creates an impermeable boundary between different user sessions. The OS never shares session_state between contexts carrying different session_id values. Each unique identifier operates in its own isolated namespace, preventing data leakage even when multiple users execute workflows concurrently on the same infrastructure.
Persistent Session Storage with TeamSession
For workflows requiring continuity across multiple invocations, Agno provides persistent storage through the TeamSession class in libs/agno/agno/session/team.py. This object maintains a session_data mapping that survives individual request lifecycles.
When a run completes, the AgentOS writes the mutated session_state back to TeamSession.session_data and persists it to the configured storage backend (such as a vector database). On subsequent invocations with the same session_id, the runtime loads the stored state into a fresh RunContext, restoring the user's previous context while leaving other sessions untouched:
# Team runtime loads stored state (pseudo-code)
session = TeamSession(session_id="bob-session")
if session.session_data.get("session_state"):
run_context.session_state = session.session_data["session_state"]
# After the workflow finishes, the runtime writes back any changes
session.session_data["session_state"] = run_context.session_state
session.save() # persists to the configured DB
The test suite in libs/agno/tests/unit/team/test_team_run_regressions.py validates that paused and resumed team runs correctly restore session_state across distinct execution boundaries.
Configuring Session State Behavior
While session state injection is enabled by default for multi-turn conversations and stateful workflows, callers can explicitly opt out via the add_session_state_to_context flag on RunOptions. Disabling this flag creates truly stateless executions where the session_state dictionary remains empty or unmanaged, suitable for simple one-shot inference tasks that require no memory between calls.
Summary
- Per-request isolation: Every execution receives a unique
RunContextwith a distinctsession_id, ensuring user data remains separated at the framework level. - Shared intra-run state: Parallel steps within the same
RunContextoperate on identicalsession_stateobject references, enabling coordination without external storage. - Persistent continuity: The
TeamSessionclass inlibs/agno/agno/session/team.pysaves and restoressession_stateacross multiple runs for the samesession_id. - Explicit opt-out: Developers can disable automatic state management using the
add_session_state_to_contextflag for fully stateless workflows.
Frequently Asked Questions
How does Agno prevent session state from leaking between different users?
Agno enforces isolation by requiring every workflow execution to specify a session_id within the RunContext. The framework only retrieves and persists session_state data associated with that specific identifier. Since each user receives a unique session_id (typically generated per-request or per-user), their session_state dictionaries exist in separate namespaces within the TeamSession storage layer, making cross-session data leakage impossible under normal operation.
Can parallel steps within the same workflow modify shared session state?
Yes. Parallel steps executed within a single RunContext share the same session_state object reference (via shallow copy). As confirmed in test_parallel_run_context_isolation.py, modifications made by one parallel branch are immediately visible to sibling branches because they reference the identical Python dictionary object. This allows concurrent steps to coordinate through shared counters, flags, or accumulated results.
Where is session state persisted between workflow runs?
Session state persists in the session_data attribute of the TeamSession class located in libs/agno/agno/session/team.py. When a workflow run terminates, the AgentOS serializes the final session_state dictionary into this attribute and commits it to the configured storage backend. On the next invocation with the matching session_id, the framework deserializes this data back into the new RunContext, restoring the conversation or workflow history.
Is it possible to run workflows without session state entirely?
Yes. Developers can disable automatic session state injection by setting add_session_state_to_context to False in the RunOptions configuration. When this flag is disabled, the RunContext either receives an empty session_state dictionary or bypasses state management logic entirely, creating a stateless execution environment suitable for one-shot tasks that require no memory of previous interactions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →