diff --git a/dotnet/src/Microsoft.Agents.AI/Compaction/ChatStrategyExtensions.cs b/dotnet/src/Microsoft.Agents.AI/Compaction/ChatStrategyExtensions.cs
new file mode 100644
index 0000000000..b7f224d751
--- /dev/null
+++ b/dotnet/src/Microsoft.Agents.AI/Compaction/ChatStrategyExtensions.cs
@@ -0,0 +1,59 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.AI;
+using Microsoft.Shared.DiagnosticIds;
+using Microsoft.Shared.Diagnostics;
+
+namespace Microsoft.Agents.AI.Compaction;
+
+///
+/// Provides extension methods for .
+///
+[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
+public static class ChatStrategyExtensions
+{
+ ///
+ /// Returns an that applies this to reduce a list of messages.
+ ///
+ /// The compaction strategy to wrap as an .
+ ///
+ /// An that, on each call to , builds a
+ /// from the supplied messages and applies the strategy's compaction logic,
+ /// returning the resulting included messages.
+ ///
+ ///
+ /// This allows any to be used wherever an is expected,
+ /// bridging the compaction pipeline into systems bound to the Microsoft.Extensions.AI contract.
+ ///
+ public static IChatReducer AsChatReducer(this CompactionStrategy strategy)
+ {
+ Throw.IfNull(strategy);
+
+ return new CompactionStrategyChatReducer(strategy);
+ }
+
+ ///
+ /// An adapter that delegates to a .
+ ///
+ private sealed class CompactionStrategyChatReducer : IChatReducer
+ {
+ private readonly CompactionStrategy _strategy;
+
+ public CompactionStrategyChatReducer(CompactionStrategy strategy)
+ {
+ this._strategy = strategy;
+ }
+
+ ///
+ public async Task> ReduceAsync(IEnumerable messages, CancellationToken cancellationToken = default)
+ {
+ CompactionMessageIndex index = CompactionMessageIndex.Create([.. messages]);
+ await this._strategy.CompactAsync(index, cancellationToken: cancellationToken).ConfigureAwait(false);
+ return index.GetIncludedMessages();
+ }
+ }
+}
diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/Compaction/ChatStrategyExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Compaction/ChatStrategyExtensionsTests.cs
new file mode 100644
index 0000000000..195d5756e5
--- /dev/null
+++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/Compaction/ChatStrategyExtensionsTests.cs
@@ -0,0 +1,159 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Agents.AI.Compaction;
+using Microsoft.Extensions.AI;
+
+namespace Microsoft.Agents.AI.UnitTests.Compaction;
+
+///
+/// Contains tests for the class.
+///
+public class ChatStrategyExtensionsTests
+{
+ [Fact]
+ public void AsChatReducerNullStrategyThrows()
+ {
+ // Act & Assert
+ Assert.Throws(() => ((CompactionStrategy)null!).AsChatReducer());
+ }
+
+ [Fact]
+ public void AsChatReducerReturnsIChatReducer()
+ {
+ // Arrange
+ ChatReducerCompactionStrategy strategy = new(new IdentityReducer(), CompactionTriggers.Always);
+
+ // Act
+ IChatReducer reducer = strategy.AsChatReducer();
+
+ // Assert
+ Assert.NotNull(reducer);
+ }
+
+ [Fact]
+ public async Task ReduceAsyncReturnsAllMessagesWhenStrategyDoesNotCompactAsync()
+ {
+ // Arrange — trigger never fires, so no compaction occurs
+ ChatReducerCompactionStrategy strategy = new(new IdentityReducer(), CompactionTriggers.Never);
+ IChatReducer reducer = strategy.AsChatReducer();
+
+ List messages =
+ [
+ new(ChatRole.User, "Hello"),
+ new(ChatRole.Assistant, "Hi!"),
+ ];
+
+ // Act
+ IEnumerable result = await reducer.ReduceAsync(messages, CancellationToken.None);
+
+ // Assert
+ Assert.Equal(messages, result);
+ }
+
+ [Fact]
+ public async Task ReduceAsyncCompactsMessagesWhenStrategyFiresAsync()
+ {
+ // Arrange — reducer keeps only the last message
+ ChatReducerCompactionStrategy strategy = new(
+ new TakeLastReducer(1),
+ CompactionTriggers.Always);
+ IChatReducer reducer = strategy.AsChatReducer();
+
+ List messages =
+ [
+ new(ChatRole.User, "First"),
+ new(ChatRole.Assistant, "Response 1"),
+ new(ChatRole.User, "Second"),
+ ];
+
+ // Act
+ IEnumerable result = await reducer.ReduceAsync(messages, CancellationToken.None);
+
+ // Assert
+ List resultList = [.. result];
+ Assert.Single(resultList);
+ Assert.Equal("Second", resultList[0].Text);
+ }
+
+ [Fact]
+ public async Task ReduceAsyncPassesCancellationTokenToStrategyAsync()
+ {
+ // Arrange
+ using CancellationTokenSource cts = new();
+ CancellationToken capturedToken = default;
+
+ CapturingReducer capturingReducer = new(token => capturedToken = token);
+ ChatReducerCompactionStrategy strategy = new(capturingReducer, CompactionTriggers.Always);
+ IChatReducer reducer = strategy.AsChatReducer();
+
+ List messages =
+ [
+ new(ChatRole.User, "Hello"),
+ new(ChatRole.User, "World"),
+ ];
+
+ // Act
+ await reducer.ReduceAsync(messages, cts.Token);
+
+ // Assert
+ Assert.Equal(cts.Token, capturedToken);
+ }
+
+ [Fact]
+ public async Task ReduceAsyncEmptyMessagesReturnsEmptyAsync()
+ {
+ // Arrange
+ ChatReducerCompactionStrategy strategy = new(new IdentityReducer(), CompactionTriggers.Always);
+ IChatReducer reducer = strategy.AsChatReducer();
+
+ // Act
+ IEnumerable result = await reducer.ReduceAsync([], CancellationToken.None);
+
+ // Assert
+ Assert.Empty(result);
+ }
+
+ ///
+ /// An that returns messages unchanged.
+ ///
+ private sealed class IdentityReducer : IChatReducer
+ {
+ public Task> ReduceAsync(IEnumerable messages, CancellationToken cancellationToken = default)
+ => Task.FromResult(messages);
+ }
+
+ ///
+ /// An that keeps only the last n messages.
+ ///
+ private sealed class TakeLastReducer : IChatReducer
+ {
+ private readonly int _count;
+
+ public TakeLastReducer(int count) => this._count = count;
+
+ public Task> ReduceAsync(IEnumerable messages, CancellationToken cancellationToken = default)
+ => Task.FromResult(messages.Reverse().Take(this._count));
+ }
+
+ ///
+ /// An that captures the passed to .
+ ///
+ private sealed class CapturingReducer : IChatReducer
+ {
+ private readonly Action _capture;
+
+ public CapturingReducer(Action capture) => this._capture = capture;
+
+ public Task> ReduceAsync(IEnumerable messages, CancellationToken cancellationToken = default)
+ {
+ this._capture(cancellationToken);
+ IEnumerable reducedMessages = [messages.Reverse().First()];
+ return Task.FromResult(reducedMessages);
+ }
+ }
+}