-
Notifications
You must be signed in to change notification settings - Fork 2.3k
.NET: [PREVIEW BREAKING] Promote AgentSessionStore into Agents.AI.Abstractions
#7991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Roger Barreto (rogerbarreto)
wants to merge
8
commits into
microsoft:main
Choose a base branch
from
rogerbarreto:rogerbarreto-agent-session-store
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
21d1ee9
feat(dotnet): share agent session store abstraction
rogerbarreto 973592f
fix(dotnet): address session store review findings
rogerbarreto c1961d5
fix(dotnet): remove legacy session key fallback
rogerbarreto b7ff84b
feat(dotnet): add partitioned session store keys
rogerbarreto c5fa9fe
refactor(dotnet): keep session keys storage agnostic
rogerbarreto 925b947
refactor(dotnet): promote delegating session store
rogerbarreto 9bddbe9
Restore session store service discovery
rogerbarreto da5e978
Merge main into session store abstraction proposal
rogerbarreto File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| --- | ||
| status: proposed | ||
| contact: rogerbarreto | ||
| date: 2026-09-01 | ||
| deciders: rogerbarreto | ||
| consulted: [] | ||
| informed: [] | ||
| --- | ||
|
|
||
| # Shared AgentSessionStore abstraction | ||
|
|
||
| ## Context and Problem Statement | ||
|
|
||
| .NET has two public `AgentSessionStore` abstract classes. `Microsoft.Agents.AI.Hosting` defines a store | ||
| whose lookup creates a session when no value exists. `Microsoft.Agents.AI.Foundry.Hosting` defines a store | ||
| whose lookup returns `null`, accepts an explicit user partition, and provides a separate convenience method | ||
| that creates a session when needed. The types cannot be used interchangeably, so storage integrations depend | ||
| on a specific hosting protocol package instead of the core agent abstractions. | ||
|
|
||
| ## Decision Drivers | ||
|
|
||
| - One storage contract must work across all hosting packages. | ||
| - Storage implementations must depend only on `Microsoft.Agents.AI.Abstractions`. | ||
| - A lookup must distinguish a missing value from a stored value without creating state as a side effect. | ||
| - Every caller must explicitly decide whether the session is partitioned by user. | ||
| - Existing Foundry storage behavior and per-user isolation must remain unchanged. | ||
|
|
||
| ## Considered Options | ||
|
|
||
| 1. Promote the Foundry Hosting contract to `Microsoft.Agents.AI.Abstractions`. | ||
| 2. Promote the conventional Hosting contract and adapt Foundry Hosting to it. | ||
| 3. Add a third contract and keep adapters for both existing contracts. | ||
|
|
||
| ## Decision Outcome | ||
|
|
||
| Chosen option: **Promote the Foundry Hosting contract to `Microsoft.Agents.AI.Abstractions`**. | ||
|
|
||
| `AgentSessionStore` moves to the `Microsoft.Agents.AI` namespace and keeps the Foundry Hosting behavior: | ||
|
|
||
| - The abstraction and every public implementation start as experimental under diagnostic `MAAI001`. | ||
| - `GetSessionAsync` returns `AgentSession?` and returns `null` when no session is stored. | ||
| - `GetOrCreateSessionAsync` performs the explicit lookup or creation operation. | ||
| - `SaveSessionAsync` and both lookup methods require a `string? userId` argument with no default value. | ||
| A non-null value must not be empty or contain only whitespace. | ||
| - `DeleteSessionAsync` and service inspection are not part of the shared contract. | ||
|
|
||
| The duplicate types in `Microsoft.Agents.AI.Hosting` and `Microsoft.Agents.AI.Foundry.Hosting` are removed. | ||
| Both packages reference the shared type directly. | ||
|
|
||
| The conventional Hosting implementations adopt the same behavior. `IsolationKeyScopedAgentSessionStore` | ||
| passes the key from `AgentIsolationKeyProvider` as the `userId` argument while leaving `conversationId` | ||
| unchanged. The in-memory and Azure Blob stores return `null` for a missing session and partition saved | ||
| sessions by user. `AIHostAgent` uses `GetOrCreateSessionAsync` when it needs a ready session. | ||
|
|
||
| ## Consequences | ||
|
|
||
| Positive: | ||
|
|
||
| - Storage implementations can be shared by Foundry Hosting, conventional Hosting, and future protocols. | ||
| - Missing session handling is explicit and consistent. | ||
| - User isolation is represented by its own argument instead of being encoded into a conversation identifier. | ||
| - `Microsoft.Agents.AI.Abstractions` owns the contract alongside `AIAgent` and `AgentSession`. | ||
|
|
||
| Negative: | ||
|
|
||
| - This is a source-breaking change for implementations of the preview Hosting contract. | ||
| - Callers must pass `userId: null` explicitly when no user partition exists. | ||
| - Consumers that need deletion must use a storage-specific API until a separate shared deletion capability is defined. | ||
|
|
||
| ## More Information | ||
|
|
||
| - [ADR-0031](0031-hosted-per-user-session-storage-isolation.md) defines the explicit user partition used by the promoted contract. | ||
| - [ADR-0032](0032-dotnet-hosting-protocol-helpers.md) records the previous conventional Hosting contract. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSessionStore.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| // Copyright (c) Microsoft. All rights reserved. | ||
|
|
||
| using System.Diagnostics.CodeAnalysis; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.Shared.DiagnosticIds; | ||
| using Microsoft.Shared.Diagnostics; | ||
|
|
||
| namespace Microsoft.Agents.AI; | ||
|
|
||
| /// <summary> | ||
| /// Defines the contract for storing and retrieving agent conversation sessions. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Implementations enable persistent storage of conversation sessions, allowing conversations to be | ||
| /// resumed across HTTP requests, application restarts, or different service instances in hosted scenarios. | ||
| /// </remarks> | ||
| [Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)] | ||
| public abstract class AgentSessionStore | ||
| { | ||
| /// <summary> | ||
| /// Saves an agent session to persistent storage. | ||
| /// </summary> | ||
| /// <param name="agent">The agent that owns this session.</param> | ||
| /// <param name="conversationId">The unique identifier for the conversation.</param> | ||
| /// <param name="session">The session to save.</param> | ||
| /// <param name="userId"> | ||
| /// The per-user partition key that scopes this session to its owner. Pass <see langword="null"/> only | ||
| /// when there is no user context, such as in a single-user application or local development. | ||
| /// Non-null values must not be empty or contain only whitespace. The parameter is required so every | ||
| /// caller consciously decides the session scope. | ||
| /// </param> | ||
| /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param> | ||
| /// <returns>A task that represents the asynchronous save operation.</returns> | ||
| public abstract ValueTask SaveSessionAsync( | ||
| AIAgent agent, | ||
| string conversationId, | ||
| AgentSession session, | ||
| string? userId, | ||
| CancellationToken cancellationToken = default); | ||
|
|
||
| /// <summary> | ||
| /// Retrieves an agent session from persistent storage, or <see langword="null"/> when no session is stored | ||
| /// for the given identifiers. | ||
| /// </summary> | ||
| /// <param name="agent">The agent that owns this session.</param> | ||
| /// <param name="conversationId">The unique identifier for the conversation to retrieve.</param> | ||
| /// <param name="userId"> | ||
| /// The per-user partition key that scopes this session to its owner. It must match the value used when the | ||
| /// session was saved. Pass <see langword="null"/> only when there is no user context. Non-null values must | ||
| /// not be empty or contain only whitespace. | ||
| /// </param> | ||
| /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param> | ||
| /// <returns> | ||
| /// A task whose result contains the restored session, or <see langword="null"/> when nothing is stored for | ||
| /// the given identifiers. This method never creates a session. | ||
| /// </returns> | ||
|
rogerbarreto marked this conversation as resolved.
|
||
| public abstract ValueTask<AgentSession?> GetSessionAsync( | ||
| AIAgent agent, | ||
| string conversationId, | ||
| string? userId, | ||
| CancellationToken cancellationToken = default); | ||
|
|
||
| /// <summary> | ||
| /// Retrieves the stored session for the given identifiers, or creates a new one when none is stored. | ||
| /// </summary> | ||
| /// <param name="agent">The agent that owns this session.</param> | ||
| /// <param name="conversationId">The unique identifier for the conversation to retrieve.</param> | ||
| /// <param name="userId">The per-user partition key; see <see cref="GetSessionAsync"/> for its meaning.</param> | ||
| /// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests.</param> | ||
| /// <returns>A task whose result is always a usable session.</returns> | ||
| public virtual async ValueTask<AgentSession> GetOrCreateSessionAsync( | ||
| AIAgent agent, | ||
| string conversationId, | ||
| string? userId, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| _ = Throw.IfNull(agent); | ||
|
|
||
| return await this.GetSessionAsync(agent, conversationId, userId, cancellationToken).ConfigureAwait(false) | ||
| ?? await agent.CreateSessionAsync(cancellationToken).ConfigureAwait(false); | ||
| } | ||
| } | ||
5 changes: 5 additions & 0 deletions
5
dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net10.0/PublicAPI.Unshipped.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,6 @@ | ||
| #nullable enable | ||
| [MAAI001]Microsoft.Agents.AI.AgentSessionStore | ||
| [MAAI001]Microsoft.Agents.AI.AgentSessionStore.AgentSessionStore() -> void | ||
| [MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.GetSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! conversationId, string? userId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<Microsoft.Agents.AI.AgentSession?> | ||
| [MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! conversationId, Microsoft.Agents.AI.AgentSession! session, string? userId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask | ||
| [MAAI001]virtual Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! conversationId, string? userId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask<Microsoft.Agents.AI.AgentSession!> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.