diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AServerServiceCollectionExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AServerServiceCollectionExtensions.cs
index deafbb2ced..cb0e15dac8 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AServerServiceCollectionExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting.A2A/A2AServerServiceCollectionExtensions.cs
@@ -28,6 +28,23 @@ public static class A2AServerServiceCollectionExtensions
/// The agent builder whose name identifies the agent.
/// An optional callback to configure .
/// The for chaining.
+ ///
+ ///
+ /// Trust model. The A2A contextId arrives from the wire
+ /// and is treated as a chain-resume identifier — not as an authorization
+ /// token. The contract carries no principal/owner
+ /// dimension, so when a persistent store is registered any caller who knows or
+ /// guesses another caller's contextId can resume that other caller's
+ /// persisted thread. Hosts that serve more than one user must compose a principal
+ /// dimension into the lookup key — typically by calling
+ /// UseClaimsBasedSessionIsolation(...) from
+ /// Microsoft.Agents.AI.Hosting.AspNetCore (or by registering a custom
+ /// ). When no isolation provider is
+ /// registered, behavior is unchanged — the bare contextId is used as the
+ /// conversation identifier, which is appropriate for first-run / single-user /
+ /// prototyping scenarios but unsafe for multi-user hosts.
+ ///
+ ///
public static IHostedAgentBuilder AddA2AServer(this IHostedAgentBuilder agentBuilder, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(agentBuilder);
@@ -46,6 +63,13 @@ public static class A2AServerServiceCollectionExtensions
/// The name of the agent to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
+ ///
+ /// See the trust-model remarks on
+ /// for guidance on multi-user hosts (the wire contextId is a chain-resume
+ /// identifier, not an authorization token; multi-user hosts must compose a
+ /// principal dimension via UseClaimsBasedSessionIsolation(...) or a custom
+ /// ).
+ ///
public static IHostApplicationBuilder AddA2AServer(this IHostApplicationBuilder builder, string agentName, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(builder);
@@ -65,6 +89,13 @@ public static class A2AServerServiceCollectionExtensions
/// The agent instance to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
+ ///
+ /// See the trust-model remarks on
+ /// for guidance on multi-user hosts (the wire contextId is a chain-resume
+ /// identifier, not an authorization token; multi-user hosts must compose a
+ /// principal dimension via UseClaimsBasedSessionIsolation(...) or a custom
+ /// ).
+ ///
public static IHostApplicationBuilder AddA2AServer(this IHostApplicationBuilder builder, AIAgent agent, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(builder);
@@ -83,6 +114,13 @@ public static class A2AServerServiceCollectionExtensions
/// The name of the agent to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
+ ///
+ /// See the trust-model remarks on
+ /// for guidance on multi-user hosts (the wire contextId is a chain-resume
+ /// identifier, not an authorization token; multi-user hosts must compose a
+ /// principal dimension via UseClaimsBasedSessionIsolation(...) or a custom
+ /// ).
+ ///
public static IServiceCollection AddA2AServer(this IServiceCollection services, string agentName, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(services);
@@ -114,6 +152,13 @@ public static class A2AServerServiceCollectionExtensions
/// The agent instance to create an A2A server for.
/// An optional callback to configure .
/// The for chaining.
+ ///
+ /// See the trust-model remarks on
+ /// for guidance on multi-user hosts (the wire contextId is a chain-resume
+ /// identifier, not an authorization token; multi-user hosts must compose a
+ /// principal dimension via UseClaimsBasedSessionIsolation(...) or a custom
+ /// ).
+ ///
public static IServiceCollection AddA2AServer(this IServiceCollection services, AIAgent agent, Action? configureOptions = null)
{
ArgumentNullException.ThrowIfNull(services);
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs
index 948ecdca42..85fd00fb8b 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/AGUIEndpointRouteBuilderExtensions.cs
@@ -73,6 +73,26 @@ public static class AGUIEndpointRouteBuilderExtensions
/// it will be used to persist conversation sessions across requests using the AG-UI thread ID as the
/// conversation identifier. If no session store is registered, sessions are ephemeral (not persisted).
///
+ ///
+ /// Trust model. The AG-UI RunAgentInput.ThreadId arrives
+ /// from the wire and is treated as a chain-resume identifier — not as an
+ /// authorization token. The contract carries no
+ /// principal/owner dimension, so when a persistent store is registered any caller
+ /// who knows or guesses another caller's ThreadId can resume that other
+ /// caller's persisted thread. Hosts that serve more than one user must compose a
+ /// principal dimension into the lookup key. The recommended way is to wrap the
+ /// keyed in
+ /// , typically by calling
+ /// UseClaimsBasedSessionIsolation(...) from
+ /// Microsoft.Agents.AI.Hosting.AspNetCore (or by registering a custom
+ /// ) and registering the store via the
+ /// WithSessionStore(...) / WithInMemorySessionStore(...) helpers on
+ /// so that the wrapper is applied. When no
+ /// isolation provider is registered, behavior is unchanged — the bare
+ /// ThreadId is used as the conversation identifier, which is appropriate
+ /// for first-run / single-user / prototyping scenarios but unsafe for
+ /// multi-user hosts.
+ ///
///
public static IEndpointConventionBuilder MapAGUI(
this IEndpointRouteBuilder endpoints,
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs
index 1d5373e38a..7c0539fe51 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs
@@ -11,9 +11,39 @@ namespace Microsoft.Agents.AI.Hosting;
/// Defines the contract for storing and retrieving agent conversation threads.
///
///
+///
/// Implementations of this interface enable persistent storage of conversation threads,
/// allowing conversations to be resumed across HTTP requests, application restarts,
/// or different service instances in hosted scenarios.
+///
+///
+/// Trust model. The conversationId passed to
+/// and typically originates
+/// from the wire (for example, an AG-UI RunAgentInput.ThreadId or an A2A
+/// contextId). It is a chain-resume identifier, not an authorization
+/// token, and the (agent, conversationId) tuple carries no principal/owner
+/// dimension. Hosts that serve more than one user from the same registered store must
+/// therefore compose a principal dimension into the lookup key, otherwise any caller
+/// who knows or guesses another caller's conversationId can resume
+/// that other caller's persisted thread. The framework provides
+/// as a decorator that rewrites
+/// conversationId to include an isolation key resolved from a
+/// (for example, the ASP.NET Core
+/// ClaimsIdentitySessionIsolationKeyProvider wired up via
+/// UseClaimsBasedSessionIsolation(...)). When no provider is registered, the
+/// store behaves as a single-namespace persistence layer — appropriate for
+/// single-user / first-run / prototyping scenarios but unsafe for multi-user hosts.
+///
+///
+/// Implementer guidance. Implementations should treat
+/// conversationId as opaque: do not parse it, do not impose length
+/// or character-set constraints on it, and do not assume it round-trips to the value
+/// the caller originally supplied (decorators such as
+/// may rewrite it before forwarding).
+/// Be aware that any logging, telemetry, or audit sink that surfaces
+/// conversationId will also surface the isolation prefix when a
+/// scoping decorator is in the chain.
+///
///
public abstract class AgentSessionStore
{
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentSessionStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentSessionStore.cs
index 9999527505..832c411977 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentSessionStore.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting/Local/InMemoryAgentSessionStore.cs
@@ -24,6 +24,20 @@ namespace Microsoft.Agents.AI.Hosting;
/// For production use with multiple instances or persistence across restarts, use a durable storage implementation
/// such as Redis, SQL Server, or Azure Cosmos DB.
///
+///
+/// Multi-user warning. This store keys threads by
+/// (agent.Id, conversationId) only — it has no principal/owner dimension. When
+/// the conversation identifier originates from the wire (for example, an AG-UI
+/// RunAgentInput.ThreadId or an A2A contextId), any caller who knows
+/// or guesses another caller's identifier can resume that other caller's persisted
+/// thread. Multi-user hosts must wrap this store in
+/// (typically by calling
+/// UseClaimsBasedSessionIsolation(...) from
+/// Microsoft.Agents.AI.Hosting.AspNetCore or by registering a custom
+/// ) so that the conversation namespace is
+/// scoped per principal. See the trust-model remarks on
+/// for the full background.
+///
///
public sealed class InMemoryAgentSessionStore : AgentSessionStore
{