diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSessionStore.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSessionStore.cs
index d4f4a74d22d..0f2c8ac1a5d 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSessionStore.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentSessionStore.cs
@@ -27,11 +27,34 @@ public abstract class AgentSessionStore
/// The session to save.
/// The to monitor for cancellation requests.
/// A task that represents the asynchronous save operation.
+#pragma warning disable RS0026 // Overloaded by a sessionId-based convenience method.
public abstract ValueTask SaveSessionAsync(
AIAgent agent,
AgentSessionStoreKey key,
AgentSession session,
CancellationToken cancellationToken = default);
+#pragma warning restore RS0026
+
+ ///
+ /// Saves an agent session to persistent storage using a session identifier without partitions.
+ ///
+ /// The agent that owns this session.
+ /// The logical session identifier.
+ /// The session to save.
+ /// The to monitor for cancellation requests.
+ /// A task that represents the asynchronous save operation.
+ ///
+ /// This is a convenience overload that creates an from
+ /// and calls .
+ ///
+#pragma warning disable RS0026 // Convenience overload that mirrors the AgentSessionStoreKey-based method.
+ public ValueTask SaveSessionAsync(
+ AIAgent agent,
+ string sessionId,
+ AgentSession session,
+ CancellationToken cancellationToken = default)
+ => this.SaveSessionAsync(agent, new AgentSessionStoreKey(sessionId), session, cancellationToken);
+#pragma warning restore RS0026
///
/// Retrieves an agent session from persistent storage, or when no session is stored
@@ -50,10 +73,35 @@ public abstract ValueTask SaveSessionAsync(
/// branches observing one another's changes or modifying the stored state. Implementations that cache a
/// live session must return an independent copy rather than the shared instance.
///
+#pragma warning disable RS0026 // Overloaded by a sessionId-based convenience method.
public abstract ValueTask GetSessionAsync(
AIAgent agent,
AgentSessionStoreKey key,
CancellationToken cancellationToken = default);
+#pragma warning restore RS0026
+
+ ///
+ /// Retrieves an agent session from persistent storage using a session identifier without partitions,
+ /// or when no session is stored for the given identifier.
+ ///
+ /// The agent that owns this session.
+ /// The logical session identifier.
+ /// The to monitor for cancellation requests.
+ ///
+ /// A task whose result contains the restored session, or when nothing is stored for
+ /// the given identifier. This method never creates a session.
+ ///
+ ///
+ /// This is a convenience overload that creates an from
+ /// and calls .
+ ///
+#pragma warning disable RS0026 // Convenience overload that mirrors the AgentSessionStoreKey-based method.
+ public ValueTask GetSessionAsync(
+ AIAgent agent,
+ string sessionId,
+ CancellationToken cancellationToken = default)
+ => this.GetSessionAsync(agent, new AgentSessionStoreKey(sessionId), cancellationToken);
+#pragma warning restore RS0026
///
/// Retrieves the stored session for the given identifiers, or creates a new one when none is stored.
@@ -63,10 +111,12 @@ public abstract ValueTask SaveSessionAsync(
/// The to monitor for cancellation requests.
/// A task whose result is always a usable session.
///
- /// The default implementation calls and creates a session through
- /// only when the lookup returns .
- /// Implementations that override receive this behavior automatically.
+ /// The default implementation calls
+ /// and creates a session through only when the lookup returns
+ /// . Implementations that override
+ /// receive this behavior automatically.
///
+#pragma warning disable RS0026 // Overloaded by a sessionId-based convenience method.
public virtual async ValueTask GetOrCreateSessionAsync(
AIAgent agent,
AgentSessionStoreKey key,
@@ -77,6 +127,27 @@ public virtual async ValueTask GetOrCreateSessionAsync(
return await this.GetSessionAsync(agent, key, cancellationToken).ConfigureAwait(false)
?? await agent.CreateSessionAsync(cancellationToken).ConfigureAwait(false);
}
+#pragma warning restore RS0026
+
+ ///
+ /// Retrieves the stored session for the given session identifier without partitions, or creates a new one
+ /// when none is stored.
+ ///
+ /// The agent that owns this session.
+ /// The logical session identifier.
+ /// The to monitor for cancellation requests.
+ /// A task whose result is always a usable session.
+ ///
+ /// This is a convenience overload that creates an from
+ /// and calls .
+ ///
+#pragma warning disable RS0026 // Convenience overload that mirrors the AgentSessionStoreKey-based method.
+ public ValueTask GetOrCreateSessionAsync(
+ AIAgent agent,
+ string sessionId,
+ CancellationToken cancellationToken = default)
+ => this.GetOrCreateSessionAsync(agent, new AgentSessionStoreKey(sessionId), cancellationToken);
+#pragma warning restore RS0026
///
/// Deletes a stored agent session, if present.
@@ -88,10 +159,31 @@ public virtual async ValueTask GetOrCreateSessionAsync(
///
/// Implementations delete the session and treat a missing session as a no-op.
///
+#pragma warning disable RS0026 // Overloaded by a sessionId-based convenience method.
public abstract ValueTask DeleteSessionAsync(
AIAgent agent,
AgentSessionStoreKey key,
CancellationToken cancellationToken = default);
+#pragma warning restore RS0026
+
+ ///
+ /// Deletes a stored agent session identified by a session identifier without partitions, if present.
+ ///
+ /// The agent that owns this session.
+ /// The logical session identifier.
+ /// The to monitor for cancellation requests.
+ /// A task that represents the asynchronous delete operation.
+ ///
+ /// This is a convenience overload that creates an from
+ /// and calls .
+ ///
+#pragma warning disable RS0026 // Convenience overload that mirrors the AgentSessionStoreKey-based method.
+ public ValueTask DeleteSessionAsync(
+ AIAgent agent,
+ string sessionId,
+ CancellationToken cancellationToken = default)
+ => this.DeleteSessionAsync(agent, new AgentSessionStoreKey(sessionId), cancellationToken);
+#pragma warning restore RS0026
/// Asks the store for an object of the specified type.
/// The type of object being requested.
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net10.0/PublicAPI.Unshipped.txt b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net10.0/PublicAPI.Unshipped.txt
index 085b5279485..e68014631c9 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net10.0/PublicAPI.Unshipped.txt
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net10.0/PublicAPI.Unshipped.txt
@@ -7,6 +7,10 @@
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]virtual Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.AgentSessionStoreKey(string! sessionId, System.Collections.Generic.IReadOnlyDictionary? partitions = null) -> void
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.Equals(Microsoft.Agents.AI.AgentSessionStoreKey? other) -> bool
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net472/PublicAPI.Unshipped.txt b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net472/PublicAPI.Unshipped.txt
index 085b5279485..e68014631c9 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net472/PublicAPI.Unshipped.txt
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net472/PublicAPI.Unshipped.txt
@@ -7,6 +7,10 @@
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]virtual Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.AgentSessionStoreKey(string! sessionId, System.Collections.Generic.IReadOnlyDictionary? partitions = null) -> void
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.Equals(Microsoft.Agents.AI.AgentSessionStoreKey? other) -> bool
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net8.0/PublicAPI.Unshipped.txt
index 085b5279485..e68014631c9 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net8.0/PublicAPI.Unshipped.txt
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net8.0/PublicAPI.Unshipped.txt
@@ -7,6 +7,10 @@
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]virtual Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.AgentSessionStoreKey(string! sessionId, System.Collections.Generic.IReadOnlyDictionary? partitions = null) -> void
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.Equals(Microsoft.Agents.AI.AgentSessionStoreKey? other) -> bool
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net9.0/PublicAPI.Unshipped.txt b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net9.0/PublicAPI.Unshipped.txt
index 085b5279485..e68014631c9 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net9.0/PublicAPI.Unshipped.txt
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/net9.0/PublicAPI.Unshipped.txt
@@ -7,6 +7,10 @@
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]virtual Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.AgentSessionStoreKey(string! sessionId, System.Collections.Generic.IReadOnlyDictionary? partitions = null) -> void
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.Equals(Microsoft.Agents.AI.AgentSessionStoreKey? other) -> bool
diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
index 085b5279485..e68014631c9 100644
--- a/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
@@ -7,6 +7,10 @@
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]abstract Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]virtual Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, Microsoft.Agents.AI.AgentSessionStoreKey! key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.SaveSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, Microsoft.Agents.AI.AgentSession! session, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.DeleteSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
+[MAAI001]Microsoft.Agents.AI.AgentSessionStore.GetOrCreateSessionAsync(Microsoft.Agents.AI.AIAgent! agent, string! sessionId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.AgentSessionStoreKey(string! sessionId, System.Collections.Generic.IReadOnlyDictionary? partitions = null) -> void
[MAAI001]Microsoft.Agents.AI.AgentSessionStoreKey.Equals(Microsoft.Agents.AI.AgentSessionStoreKey? other) -> bool
diff --git a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentSessionStoreTests.cs b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentSessionStoreTests.cs
index d113eb9110c..f3ce91ebdf3 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentSessionStoreTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Abstractions.UnitTests/AgentSessionStoreTests.cs
@@ -120,6 +120,108 @@ await Assert.ThrowsAsync(
() => store.GetOrCreateSessionAsync(null!, new AgentSessionStoreKey("conversation-1")).AsTask());
}
+ [Fact]
+ public async Task GetSessionAsync_SessionIdOverload_ForwardsKeyWithoutPartitionsAsync()
+ {
+ // Arrange
+ var storedSession = new TestAgentSession();
+ var agent = new Mock().Object;
+ using var cts = new CancellationTokenSource();
+ var store = new Mock();
+ store.Setup(s => s.GetSessionAsync(agent, It.IsAny(), cts.Token))
+ .ReturnsAsync(storedSession);
+
+ // Act
+ AgentSession? session = await store.Object.GetSessionAsync(agent, "conversation-1", cts.Token);
+
+ // Assert
+ Assert.Same(storedSession, session);
+ store.Verify(
+ s => s.GetSessionAsync(agent, It.Is(k => IsUnpartitionedKey(k, "conversation-1")), cts.Token),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task SaveSessionAsync_SessionIdOverload_ForwardsKeyWithoutPartitionsAsync()
+ {
+ // Arrange
+ var session = new TestAgentSession();
+ var agent = new Mock().Object;
+ using var cts = new CancellationTokenSource();
+ var store = new Mock();
+ store.Setup(s => s.SaveSessionAsync(agent, It.IsAny(), session, cts.Token))
+ .Returns(default(ValueTask));
+
+ // Act
+ await store.Object.SaveSessionAsync(agent, "conversation-1", session, cts.Token);
+
+ // Assert
+ store.Verify(
+ s => s.SaveSessionAsync(agent, It.Is(k => IsUnpartitionedKey(k, "conversation-1")), session, cts.Token),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task GetOrCreateSessionAsync_SessionIdOverload_ForwardsKeyWithoutPartitionsAsync()
+ {
+ // Arrange
+ var storedSession = new TestAgentSession();
+ var agent = new Mock().Object;
+ using var cts = new CancellationTokenSource();
+ var store = new Mock();
+ store.Setup(s => s.GetOrCreateSessionAsync(agent, It.IsAny(), cts.Token))
+ .ReturnsAsync(storedSession);
+
+ // Act
+ AgentSession session = await store.Object.GetOrCreateSessionAsync(agent, "conversation-1", cts.Token);
+
+ // Assert
+ Assert.Same(storedSession, session);
+ store.Verify(
+ s => s.GetOrCreateSessionAsync(agent, It.Is(k => IsUnpartitionedKey(k, "conversation-1")), cts.Token),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task DeleteSessionAsync_SessionIdOverload_ForwardsKeyWithoutPartitionsAsync()
+ {
+ // Arrange
+ var agent = new Mock().Object;
+ using var cts = new CancellationTokenSource();
+ var store = new Mock();
+ store.Setup(s => s.DeleteSessionAsync(agent, It.IsAny(), cts.Token))
+ .Returns(default(ValueTask));
+
+ // Act
+ await store.Object.DeleteSessionAsync(agent, "conversation-1", cts.Token);
+
+ // Assert
+ store.Verify(
+ s => s.DeleteSessionAsync(agent, It.Is(k => IsUnpartitionedKey(k, "conversation-1")), cts.Token),
+ Times.Once);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData(" ")]
+ public async Task SessionIdOverloads_InvalidSessionId_ThrowAsync(string? sessionId)
+ {
+ // Arrange
+ var store = new TestAgentSessionStore(session: null);
+ var agent = new Mock().Object;
+ var session = new TestAgentSession();
+
+ // Act and assert
+ await Assert.ThrowsAnyAsync(() => store.GetSessionAsync(agent, sessionId!).AsTask());
+ await Assert.ThrowsAnyAsync(() => store.SaveSessionAsync(agent, sessionId!, session).AsTask());
+ await Assert.ThrowsAnyAsync(() => store.GetOrCreateSessionAsync(agent, sessionId!).AsTask());
+ await Assert.ThrowsAnyAsync(() => store.DeleteSessionAsync(agent, sessionId!).AsTask());
+ }
+
+ private static bool IsUnpartitionedKey(AgentSessionStoreKey key, string sessionId)
+ => string.Equals(key.SessionId, sessionId, StringComparison.Ordinal) && key.Partitions is null;
+
private sealed class TestAgentSessionStore(AgentSession? session) : AgentSessionStore
{
public AgentSessionStoreKey? LastKey { get; private set; }