Documentation Index
Fetch the complete documentation index at: https://docs.agentium.in/llms.txt
Use this file to discover all available pages before exploring further.
Multi-Tenant Primitives
The problem
A single Agentium process serving a SaaS product needs to:- Isolate tenants. Customer A’s memory must never bleed into customer B’s session.
- Isolate users within a tenant. Alice’s
userMemory.preferencesmust not be visible to Bob. - Share infrastructure. One model client, one StorageDriver, one process — but logically partitioned data.
AgentFactory/TeamFactory/WorkflowFactory— construct scope-aware agents per request.ScopedStorage— namespace every read/write on anyStorageDriverby tenant + user.
ScopedStorage
The lowest-level primitive. Wraps any StorageDriver and rewrites every namespace by prefixing with tenant + user identifiers.
Namespace transformation
| Scope | Resulting namespace |
|---|---|
{ tenantId: "acme", userId: "u-alice" } | tenant:acme:user:u-alice:<ns> |
{ tenantId: "acme" } (no user) | tenant:acme:<ns> |
{ userId: "alice" } (no tenant) | user:alice:<ns> |
{} (empty) | <ns> (no prefix; identical to raw driver) |
Methods
ScopedStorage implements the full StorageDriver interface — get, set, delete, list, initialize, close. Every call goes through the namespace rewrite. initialize() and close() delegate to the inner driver (so calling close() on the scoped wrapper closes the SHARED driver — be careful in production).
Use directly without a factory
If you don’t needAgentFactory’s sugar, just construct a fresh agent per request with a ScopedStorage:
AgentFactory
Sugar over ScopedStorage. Define the agent template once, call factory.create(scope) per request to materialize a scoped Agent.
Construction
AgentConfig you pass is the template. Don’t include userId or any per-user state in the template — that’s what the factory injects.
Per-request agent creation
factory.create({ ... }) returns a new Agent(...) whose:
userIdis set to the scoped user (used byuserMemoryand event tracing).memory.storageis wrapped inScopedStorage.checkpointing.storage,culture.storage,versioning.storageare also wrapped if present.register: falseis forced so the agent doesn’t pollute the globalregistry.
create() per request.
Method signature
create() with no args returns an unscoped Agent (useful for admin / cron jobs).
What gets scoped automatically
| AgentConfig field | Wrapped in ScopedStorage? |
|---|---|
memory.storage | ✅ |
checkpointing (when set as { storage }) | ✅ |
culture.storage | ✅ |
versioning.storage | ✅ |
costTracker | ❌ (you tag costs per scope yourself) |
semanticCache | ❌ (you decide whether to share or scope) |
Custom tools that hold their own storage | ❌ (your responsibility) |
ScopedStorage manually before adding them to the template.
TeamFactory
Identical pattern for Team:
memory.storage the same way. The team’s members are passed by reference; if they have their own scoped storage, that’s preserved.
WorkflowFactory
WorkflowFactory mostly disables global registration. The per-step agents (referenced by the workflow) carry whatever scope they were constructed with.
Putting it together — full SaaS pattern
Operational notes
- One storage driver per process. Multiple
ScopedStorageinstances share one underlying driver — keep the driver alive for the process lifetime. - Don’t close the inner driver via the scoped wrapper. Adding a check to prevent accidental close-via-wrap is on the roadmap; for now, just call
rawStorage.close()directly at shutdown. - For metrics / cost tracking, set
tenantIdonRunContext.metadataso observability picks it up. The factory already does this when you useuserId— extend totenantIdin your hooks. - Test isolation explicitly. A unit test that writes under tenant A and reads under tenant B (expecting null) catches regressions early.
See also
- Sessions and Memory — how sessions interact with scoped storage
- Incremental Session Manager — pair with
ScopedStoragefor cheap multi-tenant sessions - Cost Tracking — per-tenant budgets