From 96d242fa7f47abfa322b5413ec5b7cd0b9a37531 Mon Sep 17 00:00:00 2001
From: westey <164392973+westey-m@users.noreply.github.com>
Date: Tue, 9 Jun 2026 14:06:00 +0100
Subject: [PATCH] .NET: Remove required token params from HarnessAgent, make
compaction opt-in (#6409)
* Move token params from HarnessAgent constructor to options
Remove the required maxContextWindowTokens and maxOutputTokens
constructor parameters from HarnessAgent and AsHarnessAgent, replacing
them with optional MaxContextWindowTokens and MaxOutputTokens properties
on HarnessAgentOptions.
When both values are provided, compaction is enabled as before (in-loop
CompactionProvider and chat reducer on the default InMemoryChatHistory
Provider). When either is null, compaction is disabled entirely, making
it opt-in.
New constructor: HarnessAgent(IChatClient, HarnessAgentOptions?,
ILoggerFactory?, IServiceProvider?)
Closes #6333
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Improving comments.
* feat: Add custom CompactionStrategy and DisableCompaction to HarnessAgentOptions
Allow users to provide their own CompactionStrategy via options, with
a clear priority system:
1. DisableCompaction=true: no compaction regardless of other settings
2. Custom CompactionStrategy provided: use it (token params ignored)
3. Both MaxContextWindowTokens and MaxOutputTokens set: default strategy
4. Otherwise: no compaction
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: Address PR review comments on compaction opt-in
- Update chatClient param XML doc to reflect compaction is opt-in
- Strengthen compaction tests to assert ChatReducer is null/not-null
rather than just asserting construction succeeds
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../Harness_Step01_Research/Program.cs | 4 +-
.../Program.cs | 8 +-
.../Harness_Step03_DataProcessing/Program.cs | 4 +-
.../Harness_Step04_CodeExecution/Program.cs | 4 +-
.../ChatClientHarnessExtensions.cs | 19 +-
.../HarnessAgent.cs | 152 ++++++-----
.../HarnessAgentOptions.cs | 69 ++++-
.../HarnessAgentTests.cs | 236 ++++++++++++------
8 files changed, 346 insertions(+), 150 deletions(-)
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step01_Research/Program.cs b/dotnet/samples/02-agents/Harness/Harness_Step01_Research/Program.cs
index 4c4010f0c0..a1d8aca1b8 100644
--- a/dotnet/samples/02-agents/Harness/Harness_Step01_Research/Program.cs
+++ b/dotnet/samples/02-agents/Harness/Harness_Step01_Research/Program.cs
@@ -79,8 +79,10 @@ AIAgent agent =
.GetProjectOpenAIClient()
.GetResponsesClient()
.AsIChatClient(deploymentName)
- .AsHarnessAgent(MaxContextWindowTokens, MaxOutputTokens, new HarnessAgentOptions
+ .AsHarnessAgent(new HarnessAgentOptions
{
+ MaxContextWindowTokens = MaxContextWindowTokens,
+ MaxOutputTokens = MaxOutputTokens,
Name = "ResearchAgent",
Description = "A research assistant that plans and executes research tasks.",
DisableFileAccess = true, // If enabled, this would allow the agent to read/write files in a working directory
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step02_Research_WithBackgroundAgents/Program.cs b/dotnet/samples/02-agents/Harness/Harness_Step02_Research_WithBackgroundAgents/Program.cs
index e8e10a3620..654c169850 100644
--- a/dotnet/samples/02-agents/Harness/Harness_Step02_Research_WithBackgroundAgents/Program.cs
+++ b/dotnet/samples/02-agents/Harness/Harness_Step02_Research_WithBackgroundAgents/Program.cs
@@ -44,8 +44,10 @@ AIAgent webSearchAgent =
.GetProjectOpenAIClient()
.GetResponsesClient()
.AsIChatClient(deploymentName)
- .AsHarnessAgent(MaxContextWindowTokens, MaxOutputTokens, new HarnessAgentOptions
+ .AsHarnessAgent(new HarnessAgentOptions
{
+ MaxContextWindowTokens = MaxContextWindowTokens,
+ MaxOutputTokens = MaxOutputTokens,
Name = "WebSearchAgent",
Description = "An agent that can search the web to find information.",
OpenTelemetrySourceName = TracingSourceName,
@@ -92,8 +94,10 @@ AIAgent parentAgent =
.GetProjectOpenAIClient()
.GetResponsesClient()
.AsIChatClient(deploymentName)
- .AsHarnessAgent(MaxContextWindowTokens, MaxOutputTokens, new HarnessAgentOptions
+ .AsHarnessAgent(new HarnessAgentOptions
{
+ MaxContextWindowTokens = MaxContextWindowTokens,
+ MaxOutputTokens = MaxOutputTokens,
Name = "StockPriceResearcher",
Description = "An agent that researches stock prices using background agents.",
OpenTelemetrySourceName = TracingSourceName,
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/Program.cs b/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/Program.cs
index 5b8d388dc8..6b88d708f2 100644
--- a/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/Program.cs
+++ b/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/Program.cs
@@ -68,8 +68,10 @@ AIAgent agent =
.GetProjectOpenAIClient()
.GetResponsesClient()
.AsIChatClient(deploymentName)
- .AsHarnessAgent(MaxContextWindowTokens, MaxOutputTokens, new HarnessAgentOptions
+ .AsHarnessAgent(new HarnessAgentOptions
{
+ MaxContextWindowTokens = MaxContextWindowTokens,
+ MaxOutputTokens = MaxOutputTokens,
Name = "DataAnalyst",
Description = "A data analyst assistant that reads, analyzes, and processes data files.",
OpenTelemetrySourceName = TracingSourceName,
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs
index af53443c63..908e43abd7 100644
--- a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs
+++ b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs
@@ -89,8 +89,10 @@ AIAgent agent =
.GetProjectOpenAIClient()
.GetResponsesClient()
.AsIChatClient(deploymentName)
- .AsHarnessAgent(MaxContextWindowTokens, MaxOutputTokens, new HarnessAgentOptions
+ .AsHarnessAgent(new HarnessAgentOptions
{
+ MaxContextWindowTokens = MaxContextWindowTokens,
+ MaxOutputTokens = MaxOutputTokens,
Name = "CodeExecutionAgent",
Description = "A technical assistant with sandboxed code execution and skill-based workflows.",
OpenTelemetrySourceName = TracingSourceName,
diff --git a/dotnet/src/Microsoft.Agents.AI.Harness/ChatClientHarnessExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Harness/ChatClientHarnessExtensions.cs
index be66e9e635..3d5f037b2f 100644
--- a/dotnet/src/Microsoft.Agents.AI.Harness/ChatClientHarnessExtensions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Harness/ChatClientHarnessExtensions.cs
@@ -16,23 +16,16 @@ public static class ChatClientHarnessExtensions
{
///
/// Creates a new that wraps this with a pre-configured
- /// pipeline including function invocation, per-service-call chat history persistence, and in-loop compaction.
+ /// pipeline including function invocation, per-service-call chat history persistence, optional in-loop compaction, and a rich set
+ /// of default context providers and agent decorators.
///
///
/// The that provides access to the underlying AI model.
///
- ///
- /// The maximum number of tokens the model's context window supports (e.g., 1,050,000 for gpt-5.4).
- /// Used to configure the compaction strategy.
- ///
- ///
- /// The maximum number of output tokens the model can generate per response (e.g., 128,000 for gpt-5.4).
- /// Used to configure the compaction strategy.
- ///
///
/// Optional configuration options for the agent, including instructions override, tools,
- /// additional context providers, and chat history provider.
- /// When , the agent uses built-in default settings.
+ /// additional context providers, chat history provider, and compaction settings.
+ /// When , the agent uses built-in default settings with compaction disabled.
///
///
/// Optional logger factory for creating loggers used by the agent and its components.
@@ -43,10 +36,8 @@ public static class ChatClientHarnessExtensions
/// A new instance.
public static HarnessAgent AsHarnessAgent(
this IChatClient chatClient,
- int maxContextWindowTokens,
- int maxOutputTokens,
HarnessAgentOptions? options = null,
ILoggerFactory? loggerFactory = null,
IServiceProvider? services = null) =>
- new(chatClient, maxContextWindowTokens, maxOutputTokens, options, loggerFactory, services);
+ new(chatClient, options, loggerFactory, services);
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs
index 6960b755ec..d6bc16d354 100644
--- a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs
@@ -18,50 +18,65 @@ namespace Microsoft.Agents.AI;
///
/// A pre-configured that wraps a with
-/// function invocation, per-service-call chat history persistence, in-loop compaction, and a rich set
+/// function invocation, per-service-call chat history persistence, optional in-loop compaction, and a rich set
/// of default context providers and agent decorators.
///
///
///
-/// assembles the following pipeline from a caller-supplied :
+/// provides an opinionated, batteries-included agent suitable for
+/// interactive agentic scenarios such as research, coding, data analysis, and general task automation.
+/// It assembles a full pipeline from a caller-supplied so that callers
+/// only need to configure the parts they want to customize.
+///
+///
+/// Chat client pipeline (inner to outer):
///
-/// - — automatic function/tool invocation.
-/// - — allows external code to inject messages into the conversation mid-stream.
-/// - — persists chat history after every individual service call within a function-invocation loop.
-/// - with a — applies context-window compaction before each call so long function-invocation loops do not overflow the context window.
+/// - — automatic function/tool invocation with configurable iteration limits.
+/// - — allows external code to inject messages into the conversation mid-stream (e.g., for user interrupts).
+/// - — persists chat history after every individual service call within a function-invocation loop, enabling crash recovery and history inspection.
+/// - with a — applies context-window compaction before each call so long function-invocation loops do not overflow the context window. Only included when and are both provided.
///
///
///
-/// By default, the following context providers are included (each can be disabled via ):
+/// Context providers (each enabled by default, individually disableable via ):
///
-/// - — todo list management.
-/// - — agent mode tracking (plan/execute).
-/// - — file-based session memory.
-/// - — shared file access.
-/// - — skill discovery and loading.
+/// - — persistent todo list that the agent uses to track multi-step plans. Disable with .
+/// - — mode tracking (e.g., "plan" vs "execute") that the agent uses to structure its work. Disable with .
+/// - — file-based session memory allowing the agent to persist notes and artifacts across turns. Disable with .
+/// - — shared file access providing read/write tools for a working directory. Disable with .
+/// - — discovers and loads skill definitions from the file system, enabling dynamic tool sets. Disable with .
///
///
///
-/// The agent is also wrapped with the following decorators by default (each can be disabled):
+/// Optional context providers (enabled via ):
///
-/// - — "don't ask again" tool approval rules.
-/// - — OpenTelemetry instrumentation.
+/// - — enables delegation to background agents for parallel work. Enable by setting .
+/// - ShellEnvironmentProvider — injects OS/shell/CWD information and a shell execution tool. Enable by setting HarnessAgentOptions.ShellExecutor (.NET only).
///
///
///
-/// A is added to the chat options by default (can be disabled via
-/// ).
+/// Agent decorators (each enabled by default, individually disableable):
+///
+/// - — "don't ask again" tool approval rules enabling safe unattended execution. Disable with .
+/// - — OpenTelemetry instrumentation following semantic conventions for generative AI. Disable with .
+///
///
///
-/// The underlying is configured with
-/// and
-/// set to
-/// to match the manually-assembled pipeline.
+/// Default tools:
+///
+/// - — a hosted web search tool added to chat options by default. Disable with .
+///
///
///
-/// When no is supplied, the agent defaults to an
-/// whose chat reducer applies the same compaction strategy,
-/// keeping in-memory history from growing unboundedly across sessions.
+/// Chat history: When no is supplied,
+/// the agent defaults to an . If compaction is enabled, the provider
+/// is configured with a compaction-based chat reducer to keep in-memory history bounded. Otherwise, no reducer
+/// is applied.
+///
+///
+/// Default instructions: The agent includes built-in system instructions ()
+/// that guide general tool usage and reasoning patterns. These can be overridden via
+/// and combined with agent-specific instructions via .
///
///
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
@@ -90,21 +105,13 @@ public sealed class HarnessAgent : DelegatingAIAgent
///
///
/// The that provides access to the underlying AI model.
- /// The agent wraps this client in a function-invocation, per-service-call persistence,
- /// and compaction pipeline automatically.
- ///
- ///
- /// The maximum number of tokens the model's context window supports (e.g., 1,050,000 for gpt-5.4).
- /// Used to configure the compaction strategy.
- ///
- ///
- /// The maximum number of output tokens the model can generate per response (e.g., 128,000 for gpt-5.4).
- /// Used to configure the compaction strategy and to limit the model's output.
+ /// The agent wraps this client in a function-invocation and per-service-call persistence pipeline.
+ /// When compaction is enabled via , a compaction decorator is also added.
///
///
/// Optional configuration options for the agent, including instructions override, tools,
- /// additional context providers, and chat history provider.
- /// When , the agent uses built-in default settings.
+ /// additional context providers, chat history provider, and compaction settings.
+ /// When , the agent uses built-in default settings with compaction disabled.
///
///
/// Optional logger factory for creating loggers used by the agent and its components.
@@ -116,23 +123,22 @@ public sealed class HarnessAgent : DelegatingAIAgent
/// is .
///
///
- /// is not positive, or
- /// is negative or greater than or equal to .
+ /// is not positive, or
+ /// is negative or greater than or equal to
+ /// (when both are provided).
///
- public HarnessAgent(IChatClient chatClient, int maxContextWindowTokens, int maxOutputTokens, HarnessAgentOptions? options = null, ILoggerFactory? loggerFactory = null, IServiceProvider? services = null)
+ public HarnessAgent(IChatClient chatClient, HarnessAgentOptions? options = null, ILoggerFactory? loggerFactory = null, IServiceProvider? services = null)
: base(BuildAgent(
Throw.IfNull(chatClient),
- maxContextWindowTokens,
- maxOutputTokens,
options,
loggerFactory,
services))
{
}
- private static AIAgent BuildAgent(IChatClient chatClient, int maxContextWindowTokens, int maxOutputTokens, HarnessAgentOptions? options, ILoggerFactory? loggerFactory, IServiceProvider? services)
+ private static AIAgent BuildAgent(IChatClient chatClient, HarnessAgentOptions? options, ILoggerFactory? loggerFactory, IServiceProvider? services)
{
- ChatClientAgent innerAgent = BuildInnerAgent(chatClient, maxContextWindowTokens, maxOutputTokens, options, loggerFactory, services);
+ ChatClientAgent innerAgent = BuildInnerAgent(chatClient, options, loggerFactory, services);
AIAgentBuilder builder = innerAgent.AsBuilder();
@@ -149,17 +155,35 @@ public sealed class HarnessAgent : DelegatingAIAgent
return builder.Build(services);
}
- private static ChatClientAgent BuildInnerAgent(IChatClient chatClient, int maxContextWindowTokens, int maxOutputTokens, HarnessAgentOptions? options, ILoggerFactory? loggerFactory, IServiceProvider? services)
+ private static ChatClientAgent BuildInnerAgent(IChatClient chatClient, HarnessAgentOptions? options, ILoggerFactory? loggerFactory, IServiceProvider? services)
{
- var compactionStrategy = new ContextWindowCompactionStrategy(
- maxContextWindowTokens: maxContextWindowTokens,
- maxOutputTokens: maxOutputTokens);
+ // Determine compaction strategy:
+ // 1. DisableCompaction = true → no compaction
+ // 2. Custom CompactionStrategy provided → use it (ignore token params)
+ // 3. Both token params provided → build default ContextWindowCompactionStrategy
+ // 4. Otherwise → no compaction
+ CompactionStrategy? compactionStrategy = null;
+ if (options?.DisableCompaction is not true)
+ {
+ if (options?.CompactionStrategy is CompactionStrategy customStrategy)
+ {
+ compactionStrategy = customStrategy;
+ }
+ else if (options?.MaxContextWindowTokens is int maxCtx && options?.MaxOutputTokens is int maxOut)
+ {
+ compactionStrategy = new ContextWindowCompactionStrategy(
+ maxContextWindowTokens: maxCtx,
+ maxOutputTokens: maxOut);
+ }
+ }
ChatHistoryProvider chatHistoryProvider = options?.ChatHistoryProvider
- ?? new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions
- {
- ChatReducer = compactionStrategy.AsChatReducer(),
- });
+ ?? (compactionStrategy is not null
+ ? new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions
+ {
+ ChatReducer = compactionStrategy.AsChatReducer(),
+ })
+ : new InMemoryChatHistoryProvider());
string harnessInstructions = options?.HarnessInstructions ?? DefaultInstructions;
string? agentInstructions = options?.ChatOptions?.Instructions;
@@ -172,9 +196,11 @@ public sealed class HarnessAgent : DelegatingAIAgent
(false, false) => $"{harnessInstructions}\n\n{agentInstructions}",
};
- ChatOptions chatOptions = BuildChatOptions(options, instructions, maxOutputTokens);
+ ChatOptions chatOptions = BuildChatOptions(options, instructions, options?.MaxOutputTokens);
- var compactionProvider = new CompactionProvider(compactionStrategy, loggerFactory: loggerFactory);
+ CompactionProvider? compactionProvider = compactionStrategy is not null
+ ? new CompactionProvider(compactionStrategy, loggerFactory: loggerFactory)
+ : null;
IEnumerable contextProviders = BuildContextProviders(options, loggerFactory);
@@ -185,13 +211,19 @@ public sealed class HarnessAgent : DelegatingAIAgent
chatClientBuilder.UseNonApprovalRequiredFunctionBypassing();
}
- return chatClientBuilder
+ ChatClientBuilder pipeline = chatClientBuilder
.UseFunctionInvocation(loggerFactory, configure: options?.MaximumIterationsPerRequest is int maxIterations
? ficc => ficc.MaximumIterationsPerRequest = maxIterations
: null)
.UseMessageInjection()
- .UsePerServiceCallChatHistoryPersistence()
- .UseAIContextProviders(compactionProvider)
+ .UsePerServiceCallChatHistoryPersistence();
+
+ if (compactionProvider is not null)
+ {
+ pipeline = pipeline.UseAIContextProviders(compactionProvider);
+ }
+
+ return pipeline
.BuildAIAgent(new ChatClientAgentOptions
{
Id = options?.Id,
@@ -209,11 +241,15 @@ public sealed class HarnessAgent : DelegatingAIAgent
services);
}
- private static ChatOptions BuildChatOptions(HarnessAgentOptions? options, string instructions, int maxOutputTokens)
+ private static ChatOptions BuildChatOptions(HarnessAgentOptions? options, string instructions, int? maxOutputTokens)
{
ChatOptions result = options?.ChatOptions?.Clone() ?? new ChatOptions();
result.Instructions = instructions;
- result.MaxOutputTokens ??= maxOutputTokens;
+
+ if (maxOutputTokens.HasValue)
+ {
+ result.MaxOutputTokens ??= maxOutputTokens.Value;
+ }
if (options?.DisableWebSearch is not true)
{
diff --git a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs
index 85924b7c3e..291650b9b9 100644
--- a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using Microsoft.Agents.AI.Compaction;
#if NET
using Microsoft.Agents.AI.Tools.Shell;
#endif
@@ -31,6 +32,68 @@ public sealed class HarnessAgentOptions
///
public string? Description { get; set; }
+ ///
+ /// Gets or sets the maximum number of tokens the model's context window supports (e.g., 1,050,000 for gpt-5.4).
+ ///
+ ///
+ ///
+ /// When both and are provided (and no
+ /// custom is set), a default
+ /// is constructed from these values to prevent function-invocation loops from overflowing the context window.
+ ///
+ ///
+ /// Ignored when is provided or when is
+ /// .
+ ///
+ ///
+ public int? MaxContextWindowTokens { get; set; }
+
+ ///
+ /// Gets or sets the maximum number of output tokens the model can generate per response (e.g., 128,000 for gpt-5.4).
+ ///
+ ///
+ ///
+ /// When set, this value is used as the default for .
+ /// when not explicitly configured.
+ ///
+ ///
+ /// For compaction purposes, this value is used together with to construct a
+ /// default — but only when no custom
+ /// is provided and is .
+ ///
+ ///
+ public int? MaxOutputTokens { get; set; }
+
+ ///
+ /// Gets or sets a custom to use for in-loop context-window compaction.
+ ///
+ ///
+ ///
+ /// When provided, this strategy is used directly and and
+ /// are ignored for compaction purposes ( is still
+ /// used as the default for . if set).
+ ///
+ ///
+ /// When and both and
+ /// are provided, a default is constructed from those values.
+ ///
+ ///
+ /// This property is ignored when is .
+ ///
+ ///
+ public CompactionStrategy? CompactionStrategy { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether in-loop compaction is disabled.
+ ///
+ ///
+ /// When , compaction is disabled regardless of ,
+ /// , or settings. No
+ /// is added to the chat client pipeline, and the default
+ /// is configured without a chat reducer.
+ ///
+ public bool DisableCompaction { get; set; }
+
///
/// Gets or sets additional chat options such as tools for the agent to use.
///
@@ -68,9 +131,9 @@ public sealed class HarnessAgentOptions
/// Gets or sets the to use for storing chat history.
///
///
- /// When , the agent defaults to an
- /// configured with a compaction-based chat reducer derived from the maxContextWindowTokens
- /// and maxOutputTokens constructor parameters of .
+ /// When , the agent defaults to an .
+ /// If and are both provided,
+ /// the default provider is configured with a compaction-based chat reducer; otherwise, no reducer is applied.
///
public ChatHistoryProvider? ChatHistoryProvider { get; set; }
diff --git a/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs
index f7977b595f..a6b06d2a10 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs
@@ -21,9 +21,12 @@ public class HarnessAgentTests
///
/// Creates a HarnessAgent with all default features disabled to isolate tests for specific behaviors.
+ /// Compaction is enabled by default for backward compatibility with existing tests.
///
private static HarnessAgentOptions CreateAllDisabledOptions() => new()
{
+ MaxContextWindowTokens = TestMaxContextWindowTokens,
+ MaxOutputTokens = TestMaxOutputTokens,
DisableToolApproval = true,
DisableOpenTelemetry = true,
DisableFileMemory = true,
@@ -43,7 +46,7 @@ public class HarnessAgentTests
public void Constructor_ThrowsWhenChatClientIsNull()
{
// Act & Assert
- Assert.Throws(() => new HarnessAgent(null!, TestMaxContextWindowTokens, TestMaxOutputTokens));
+ Assert.Throws(() => new HarnessAgent(null!));
}
///
@@ -54,9 +57,10 @@ public class HarnessAgentTests
{
// Arrange
var chatClient = new Mock().Object;
+ var options = new HarnessAgentOptions { MaxContextWindowTokens = 0, MaxOutputTokens = TestMaxOutputTokens };
// Act & Assert
- Assert.Throws(() => new HarnessAgent(chatClient, 0, TestMaxOutputTokens));
+ Assert.Throws(() => new HarnessAgent(chatClient, options));
}
///
@@ -67,9 +71,10 @@ public class HarnessAgentTests
{
// Arrange
var chatClient = new Mock().Object;
+ var options = new HarnessAgentOptions { MaxContextWindowTokens = 100_000, MaxOutputTokens = 100_000 };
// Act & Assert
- Assert.Throws(() => new HarnessAgent(chatClient, 100_000, 100_000));
+ Assert.Throws(() => new HarnessAgent(chatClient, options));
}
///
@@ -82,7 +87,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens);
+ var agent = new HarnessAgent(chatClient);
// Assert
Assert.NotNull(agent);
@@ -105,7 +110,7 @@ public class HarnessAgentTests
options.Description = "A test agent";
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
// Assert
Assert.Equal("TestAgent", agent.Name);
@@ -124,7 +129,7 @@ public class HarnessAgentTests
options.Id = "my-agent-id";
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
// Assert
Assert.Equal("my-agent-id", agent.Id);
@@ -144,7 +149,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -164,7 +169,7 @@ public class HarnessAgentTests
options.ChatOptions = new ChatOptions { Temperature = 0.5f };
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -184,7 +189,7 @@ public class HarnessAgentTests
options.ChatOptions = new ChatOptions { Instructions = "You are a custom assistant." };
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -205,7 +210,7 @@ public class HarnessAgentTests
options.HarnessInstructions = "Custom harness rules.";
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -226,7 +231,7 @@ public class HarnessAgentTests
options.ChatOptions = new ChatOptions { Instructions = "You are a research agent." };
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -247,7 +252,7 @@ public class HarnessAgentTests
options.ChatOptions = new ChatOptions { Instructions = "Agent only instructions." };
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -267,7 +272,7 @@ public class HarnessAgentTests
options.HarnessInstructions = string.Empty;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -289,7 +294,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -310,7 +315,7 @@ public class HarnessAgentTests
options.ChatHistoryProvider = customProvider;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -332,7 +337,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -353,7 +358,7 @@ public class HarnessAgentTests
var rawClient = mockClient.Object;
// Act
- var agent = new HarnessAgent(rawClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(rawClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert — the pipeline wraps the raw client, so the outer client is not the same object.
@@ -378,7 +383,7 @@ public class HarnessAgentTests
options.AIContextProviders = [customProvider];
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert — the custom provider should appear in the inner agent's AIContextProviders.
@@ -398,7 +403,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -432,7 +437,7 @@ public class HarnessAgentTests
var options = CreateAllDisabledOptions();
options.ChatOptions = new ChatOptions { Tools = [tool] };
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(mockClient.Object, options);
var session = await agent.CreateSessionAsync();
// Act
@@ -459,8 +464,10 @@ public class HarnessAgentTests
};
// Act
- _ = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, new HarnessAgentOptions
+ _ = new HarnessAgent(chatClient, new HarnessAgentOptions
{
+ MaxContextWindowTokens = TestMaxContextWindowTokens,
+ MaxOutputTokens = TestMaxOutputTokens,
ChatOptions = sourceChatOptions,
});
@@ -483,7 +490,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
// Assert
Assert.Same(agent, agent.GetService());
@@ -499,7 +506,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
// Assert
Assert.NotNull(agent.GetService());
@@ -524,7 +531,7 @@ public class HarnessAgentTests
It.IsAny()))
.ReturnsAsync(new ChatResponse(new ChatMessage(ChatRole.Assistant, "Hello!")));
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(mockClient.Object, CreateAllDisabledOptions());
var session = await agent.CreateSessionAsync();
// Act
@@ -565,7 +572,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = chatClient.AsHarnessAgent(TestMaxContextWindowTokens, TestMaxOutputTokens);
+ var agent = chatClient.AsHarnessAgent();
// Assert
Assert.NotNull(agent);
@@ -586,7 +593,7 @@ public class HarnessAgentTests
options.ChatOptions = new ChatOptions { Instructions = "Custom instructions" };
// Act
- var agent = chatClient.AsHarnessAgent(TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = chatClient.AsHarnessAgent(options);
var innerAgent = agent.GetService();
// Assert
@@ -603,7 +610,7 @@ public class HarnessAgentTests
public void AsHarnessAgent_ThrowsWhenChatClientIsNull()
{
// Act & Assert
- Assert.Throws(() => ((IChatClient)null!).AsHarnessAgent(TestMaxContextWindowTokens, TestMaxOutputTokens));
+ Assert.Throws(() => ((IChatClient)null!).AsHarnessAgent());
}
#endregion
@@ -622,7 +629,7 @@ public class HarnessAgentTests
options.DisableToolApproval = false;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
// Assert
Assert.NotNull(agent.GetService());
@@ -638,7 +645,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
// Assert
Assert.Null(agent.GetService());
@@ -678,7 +685,7 @@ public class HarnessAgentTests
AutoApprovalRules = [fcc => new ValueTask(fcc.Name == "ReadTool")]
};
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(mockClient.Object, options);
var session = await agent.CreateSessionAsync();
// Act
@@ -721,7 +728,7 @@ public class HarnessAgentTests
var options = CreateAllDisabledOptions();
options.ChatOptions = new ChatOptions { Tools = [normalTool, approvalTool] };
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(mockClient.Object, options);
var session = await agent.CreateSessionAsync();
// Act
@@ -763,7 +770,7 @@ public class HarnessAgentTests
options.DisableNonApprovalRequiredFunctionBypassing = true;
options.ChatOptions = new ChatOptions { Tools = [normalTool, approvalTool] };
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(mockClient.Object, options);
var session = await agent.CreateSessionAsync();
// Act
@@ -796,7 +803,7 @@ public class HarnessAgentTests
options.DisableOpenTelemetry = false;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
// Assert
Assert.NotNull(agent.GetService());
@@ -812,7 +819,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
// Assert
Assert.Null(agent.GetService());
@@ -831,7 +838,7 @@ public class HarnessAgentTests
options.OpenTelemetrySourceName = "MyApp.AgentTracing";
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
// Assert
Assert.NotNull(agent.GetService());
@@ -858,7 +865,7 @@ public class HarnessAgentTests
var options = CreateAllDisabledOptions();
options.DisableWebSearch = false;
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(mockClient.Object, options);
var session = await agent.CreateSessionAsync();
// Act
@@ -883,7 +890,7 @@ public class HarnessAgentTests
.Callback, ChatOptions?, CancellationToken>((_, opts, _) => capturedOptions = opts)
.ReturnsAsync(new ChatResponse(new ChatMessage(ChatRole.Assistant, "Done")));
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(mockClient.Object, CreateAllDisabledOptions());
var session = await agent.CreateSessionAsync();
// Act
@@ -916,7 +923,7 @@ public class HarnessAgentTests
options.DisableWebSearch = false;
options.ChatOptions = new ChatOptions { Tools = [userTool] };
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(mockClient.Object, options);
var session = await agent.CreateSessionAsync();
// Act
@@ -944,7 +951,7 @@ public class HarnessAgentTests
options.DisableTodoProvider = false;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -962,7 +969,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -989,7 +996,7 @@ public class HarnessAgentTests
options.DisableAgentModeProvider = false;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1007,7 +1014,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -1038,7 +1045,7 @@ public class HarnessAgentTests
};
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert — AgentModeProvider should be present (we can't easily inspect its internal options,
@@ -1063,7 +1070,7 @@ public class HarnessAgentTests
options.DisableFileMemory = false;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1081,7 +1088,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -1106,7 +1113,7 @@ public class HarnessAgentTests
options.FileMemoryStore = customStore;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert — FileMemoryProvider should be present with the custom store.
@@ -1130,7 +1137,7 @@ public class HarnessAgentTests
options.DisableFileAccess = false;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1148,7 +1155,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -1173,7 +1180,7 @@ public class HarnessAgentTests
options.FileAccessStore = customStore;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert — FileAccessProvider should be present with the custom store.
@@ -1197,7 +1204,7 @@ public class HarnessAgentTests
options.DisableAgentSkillsProvider = false;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1215,7 +1222,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
// Assert
@@ -1240,7 +1247,7 @@ public class HarnessAgentTests
options.AgentSkillsSource = customSource;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert — AgentSkillsProvider should be present.
@@ -1264,7 +1271,7 @@ public class HarnessAgentTests
options.MaximumIterationsPerRequest = 42;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
var ficc = innerAgent!.ChatClient.GetService();
@@ -1283,7 +1290,7 @@ public class HarnessAgentTests
var chatClient = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions());
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
var innerAgent = agent.GetService();
var ficc = innerAgent!.ChatClient.GetService();
@@ -1311,7 +1318,7 @@ public class HarnessAgentTests
.ReturnsAsync(new ChatResponse(new ChatMessage(ChatRole.Assistant, "Done")));
// Act
- var agent = new HarnessAgent(mockClient.Object, TestMaxContextWindowTokens, TestMaxOutputTokens);
+ var agent = new HarnessAgent(mockClient.Object);
var innerAgent = agent.GetService();
// Assert — agent wrappers
@@ -1354,7 +1361,7 @@ public class HarnessAgentTests
options.BackgroundAgents = [bgAgentMock.Object];
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1374,7 +1381,7 @@ public class HarnessAgentTests
options.BackgroundAgents = null;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1397,7 +1404,7 @@ public class HarnessAgentTests
options.BackgroundAgents = Array.Empty();
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1428,7 +1435,7 @@ public class HarnessAgentTests
options.BackgroundAgentsProviderOptions = providerOptions;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
var bgProvider = innerAgent!.AIContextProviders!.OfType().Single();
@@ -1465,7 +1472,7 @@ public class HarnessAgentTests
options.BackgroundAgents = [agent1Mock.Object, agent2Mock.Object];
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
var bgProvider = innerAgent!.AIContextProviders!.OfType().Single();
@@ -1506,7 +1513,7 @@ public class HarnessAgentTests
options.ShellExecutor = executorMock.Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1526,7 +1533,7 @@ public class HarnessAgentTests
options.ShellExecutor = null;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert
@@ -1558,7 +1565,7 @@ public class HarnessAgentTests
options.ShellExecutor = executorMock.Object;
// Act
- var agent = new HarnessAgent(chatClientMock.Object, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClientMock.Object, options);
var session = await agent.CreateSessionAsync();
await agent.RunAsync([new ChatMessage(ChatRole.User, "Hi")], session);
@@ -1587,7 +1594,7 @@ public class HarnessAgentTests
options.ShellEnvironmentProviderOptions = envOptions;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options);
+ var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService();
// Assert — provider should exist (options wiring is validated by the provider's behavior)
@@ -1611,7 +1618,7 @@ public class HarnessAgentTests
var loggerFactory = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions(), loggerFactory);
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions(), loggerFactory);
// Assert
Assert.NotNull(agent);
@@ -1628,7 +1635,7 @@ public class HarnessAgentTests
var services = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions(), services: services);
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions(), services: services);
// Assert
Assert.NotNull(agent);
@@ -1646,7 +1653,7 @@ public class HarnessAgentTests
var services = new Mock().Object;
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions(), loggerFactory, services);
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions(), loggerFactory, services);
// Assert
Assert.NotNull(agent);
@@ -1664,7 +1671,7 @@ public class HarnessAgentTests
var services = new Mock().Object;
// Act
- var agent = chatClient.AsHarnessAgent(TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions(), loggerFactory, services);
+ var agent = chatClient.AsHarnessAgent(CreateAllDisabledOptions(), loggerFactory, services);
// Assert
Assert.NotNull(agent);
@@ -1686,6 +1693,8 @@ public class HarnessAgentTests
// Act — use options that leave CompactionProvider and AgentSkillsProvider enabled
var options = new HarnessAgentOptions
{
+ MaxContextWindowTokens = TestMaxContextWindowTokens,
+ MaxOutputTokens = TestMaxOutputTokens,
DisableToolApproval = true,
DisableOpenTelemetry = true,
DisableFileMemory = true,
@@ -1694,7 +1703,7 @@ public class HarnessAgentTests
DisableTodoProvider = true,
DisableAgentModeProvider = true,
};
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, options, mockLoggerFactory.Object);
+ var agent = new HarnessAgent(chatClient, options, mockLoggerFactory.Object);
// Assert — CreateLogger should have been called by one or more downstream components
Assert.NotNull(agent);
@@ -1716,7 +1725,7 @@ public class HarnessAgentTests
.Returns(null!);
// Act
- var agent = new HarnessAgent(chatClient, TestMaxContextWindowTokens, TestMaxOutputTokens, CreateAllDisabledOptions(), services: mockServices.Object);
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions(), services: mockServices.Object);
// Assert — the service provider should have been queried during pipeline construction
Assert.NotNull(agent);
@@ -1724,4 +1733,91 @@ public class HarnessAgentTests
}
#endregion
+
+ #region Compaction Opt-in
+
+ ///
+ /// Verify that constructing without token values succeeds (compaction disabled).
+ ///
+ [Fact]
+ public void Constructor_SucceedsWithoutTokenValues()
+ {
+ // Arrange
+ var chatClient = new Mock().Object;
+ var options = new HarnessAgentOptions
+ {
+ DisableToolApproval = true,
+ DisableOpenTelemetry = true,
+ DisableFileMemory = true,
+ DisableFileAccess = true,
+ DisableWebSearch = true,
+ DisableTodoProvider = true,
+ DisableAgentModeProvider = true,
+ DisableAgentSkillsProvider = true,
+ };
+
+ // Act
+ var agent = new HarnessAgent(chatClient, options);
+
+ // Assert — compaction should be disabled (no chat reducer)
+ var innerAgent = agent.GetService();
+ Assert.NotNull(innerAgent);
+ var historyProvider = innerAgent!.ChatHistoryProvider as InMemoryChatHistoryProvider;
+ Assert.NotNull(historyProvider);
+ Assert.Null(historyProvider!.ChatReducer);
+ }
+
+ ///
+ /// Verify that when only MaxContextWindowTokens is provided (no MaxOutputTokens), compaction is disabled.
+ ///
+ [Fact]
+ public void Constructor_SucceedsWithOnlyMaxContextWindowTokens()
+ {
+ // Arrange
+ var chatClient = new Mock().Object;
+ var options = new HarnessAgentOptions
+ {
+ MaxContextWindowTokens = TestMaxContextWindowTokens,
+ DisableToolApproval = true,
+ DisableOpenTelemetry = true,
+ DisableFileMemory = true,
+ DisableFileAccess = true,
+ DisableWebSearch = true,
+ DisableTodoProvider = true,
+ DisableAgentModeProvider = true,
+ DisableAgentSkillsProvider = true,
+ };
+
+ // Act
+ var agent = new HarnessAgent(chatClient, options);
+
+ // Assert — compaction should be disabled (only one token value provided)
+ var innerAgent = agent.GetService();
+ Assert.NotNull(innerAgent);
+ var historyProvider = innerAgent!.ChatHistoryProvider as InMemoryChatHistoryProvider;
+ Assert.NotNull(historyProvider);
+ Assert.Null(historyProvider!.ChatReducer);
+ }
+
+ ///
+ /// Verify that when both token values are provided, the agent is constructed successfully with compaction.
+ ///
+ [Fact]
+ public void Constructor_SucceedsWithBothTokenValues()
+ {
+ // Arrange
+ var chatClient = new Mock().Object;
+
+ // Act
+ var agent = new HarnessAgent(chatClient, CreateAllDisabledOptions());
+
+ // Assert — compaction should be enabled (chat reducer configured)
+ var innerAgent = agent.GetService();
+ Assert.NotNull(innerAgent);
+ var historyProvider = innerAgent!.ChatHistoryProvider as InMemoryChatHistoryProvider;
+ Assert.NotNull(historyProvider);
+ Assert.NotNull(historyProvider!.ChatReducer);
+ }
+
+ #endregion
}