Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ research before making decisions.
// Turn the chat client into a HarnessAgent. On top of Post 2's file access and approvals we add the
// four "scaling" capabilities: skills (our own provider), background agents, a confined shell, and
// CodeAct.
List<AIContextProvider> contextProviders = [skillsProvider, codeAct];
// The shell is wired up in two parts: the ShellEnvironmentProvider injects OS/shell/CWD info into the
// system prompt, and the shell tool is registered below in ChatOptions.
List<AIContextProvider> contextProviders = [skillsProvider, codeAct, new ShellEnvironmentProvider(shell)];
Comment thread
westey-m marked this conversation as resolved.
Outdated

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
Expand All @@ -170,16 +172,14 @@ research before making decisions.
DisableAgentSkillsProvider = true,
// Fan-out research is delegated to this background agent.
BackgroundAgents = [researchAgent],
// The confined shell, exposed as the approval-gated run_shell tool.
ShellExecutor = shell,
// Keep reading the portfolio frictionless while writes, trades, and shell commands still prompt.
ToolApprovalAgentOptions = new ToolApprovalAgentOptions
{
AutoApprovalRules = [FileAccessProvider.ReadOnlyToolsAutoApprovalRule],
},
// Start in "execute" mode for quick lookups and actions; switch any time with /mode plan.
AgentModeProviderOptions = new AgentModeProviderOptions { DefaultMode = "execute" },
// Our skills provider plus CodeAct.
// Our skills provider, CodeAct, and the shell environment provider.
AIContextProviders = contextProviders,
ChatOptions = new ChatOptions
{
Expand All @@ -188,6 +188,8 @@ research before making decisions.
[
StockTools.CreateGetStockPriceTool(),
TradingTools.CreatePlaceTradeTool(),
// The confined shell, exposed as the approval-gated run_shell tool.
shell.AsAIFunction(requireApproval: true),
],
Reasoning = new() { Effort = ReasoningEffort.Medium },
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Agents.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;

namespace Microsoft.Extensions.AI;

/// <summary>
/// Provides extension methods for creating a <see cref="HarnessAgent"/> from an <see cref="IChatClient"/>.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public static class ChatClientHarnessExtensions
{
/// <summary>
Expand Down
24 changes: 0 additions & 24 deletions dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using Microsoft.Agents.AI.Compaction;
#if NET
using Microsoft.Agents.AI.Tools.Shell;
#endif
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.Agents.AI;
Expand Down Expand Up @@ -51,7 +46,6 @@ namespace Microsoft.Agents.AI;
/// <list type="bullet">
/// <item><description><see cref="FileAccessProvider"/> — shared file access providing read/write tools for a working directory. Enable by setting <see cref="HarnessAgentOptions.FileAccessStore"/>; configure via <see cref="HarnessAgentOptions.FileAccessProviderOptions"/>.</description></item>
/// <item><description><see cref="BackgroundAgentsProvider"/> — enables delegation to background agents for parallel work. Enable by setting <see cref="HarnessAgentOptions.BackgroundAgents"/>.</description></item>
/// <item><description><c>ShellEnvironmentProvider</c> — injects OS/shell/CWD information and a shell execution tool. Enable by setting <c>HarnessAgentOptions.ShellExecutor</c> (.NET only).</description></item>
/// </list>
/// </para>
/// <para>
Expand Down Expand Up @@ -80,7 +74,6 @@ namespace Microsoft.Agents.AI;
/// and combined with agent-specific instructions via <see cref="ChatOptions.Instructions"/>.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class HarnessAgent : DelegatingAIAgent
{
/// <summary>
Expand Down Expand Up @@ -279,16 +272,6 @@ private static ChatOptions BuildChatOptions(HarnessAgentOptions? options, string
result.Tools.Add(new HostedWebSearchTool());
}

#if NET
if (options?.ShellExecutor is ShellExecutor shellExecutor)
{
result.Tools ??= [];
result.Tools.Add(options.ShellToolName is { } shellToolName
? shellExecutor.AsAIFunction(shellToolName, options.ShellToolDescription, !options.DisableShellToolApproval)
: shellExecutor.AsAIFunction(description: options.ShellToolDescription, requireApproval: !options.DisableShellToolApproval));
}
#endif

return result;
}

Expand Down Expand Up @@ -343,13 +326,6 @@ private static List<AIContextProvider> BuildContextProviders(HarnessAgentOptions
}
}

#if NET
if (options?.ShellExecutor is ShellExecutor shellExecutor)
{
providers.Add(new ShellEnvironmentProvider(shellExecutor, options.ShellEnvironmentProviderOptions));
}
#endif

if (options?.AIContextProviders is IEnumerable<AIContextProvider> userProviders)
{
providers.AddRange(userProviders);
Expand Down
86 changes: 11 additions & 75 deletions dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Agents.AI.Compaction;
#if NET
using Microsoft.Agents.AI.Tools.Shell;
#endif
using Microsoft.Extensions.AI;
using Microsoft.Shared.DiagnosticIds;

Expand All @@ -14,7 +11,6 @@ namespace Microsoft.Agents.AI;
/// <summary>
/// Represents configuration options for a <see cref="HarnessAgent"/>.
/// </summary>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class HarnessAgentOptions
{
/// <summary>
Expand Down Expand Up @@ -46,6 +42,7 @@ public sealed class HarnessAgentOptions
/// <see langword="true"/>.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public int? MaxContextWindowTokens { get; set; }

/// <summary>
Expand All @@ -62,6 +59,7 @@ public sealed class HarnessAgentOptions
/// is provided and <see cref="DisableCompaction"/> is <see langword="false"/>.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public int? MaxOutputTokens { get; set; }

/// <summary>
Expand All @@ -81,6 +79,7 @@ public sealed class HarnessAgentOptions
/// This property is ignored when <see cref="DisableCompaction"/> is <see langword="true"/>.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public CompactionStrategy? CompactionStrategy { get; set; }

/// <summary>
Expand All @@ -92,6 +91,7 @@ public sealed class HarnessAgentOptions
/// <see cref="CompactionProvider"/> is added to the chat client pipeline, and the default
/// <see cref="InMemoryChatHistoryProvider"/> is configured without a chat reducer.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public bool DisableCompaction { get; set; }

/// <summary>
Expand Down Expand Up @@ -162,6 +162,7 @@ public sealed class HarnessAgentOptions
/// as a single-shot agent.
/// </para>
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public IEnumerable<LoopEvaluator>? LoopEvaluators { get; set; }

/// <summary>
Expand All @@ -171,6 +172,7 @@ public sealed class HarnessAgentOptions
/// When <see langword="null"/>, the <see cref="LoopAgent"/> uses its default settings. This property is ignored
/// when <see cref="LoopEvaluators"/> is <see langword="null"/> or empty.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public LoopAgentOptions? LoopAgentOptions { get; set; }

/// <summary>
Expand Down Expand Up @@ -234,6 +236,7 @@ public sealed class HarnessAgentOptions
/// a default <see cref="FileSystemAgentFileStore"/> is created.
/// This property is ignored when <see cref="DisableFileMemory"/> is <see langword="true"/>.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public AgentFileStore? FileMemoryStore { get; set; }

/// <summary>
Expand All @@ -245,6 +248,7 @@ public sealed class HarnessAgentOptions
/// included in the agent's context providers, backed by the supplied store and configured with
/// <see cref="FileAccessProviderOptions"/> when provided.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public AgentFileStore? FileAccessStore { get; set; }

/// <summary>
Expand All @@ -254,6 +258,7 @@ public sealed class HarnessAgentOptions
/// This property is only used when <see cref="FileAccessStore"/> is set (file access is opt-in).
/// When <see langword="null"/>, the provider uses its default options.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public FileAccessProviderOptions? FileAccessProviderOptions { get; set; }

/// <summary>
Expand Down Expand Up @@ -348,6 +353,7 @@ public sealed class HarnessAgentOptions
/// (case-insensitive). If these requirements are not met, <see cref="BackgroundAgentsProvider"/> will throw
/// an <see cref="System.ArgumentException"/> during construction.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public IEnumerable<AIAgent>? BackgroundAgents { get; set; }

/// <summary>
Expand All @@ -357,76 +363,6 @@ public sealed class HarnessAgentOptions
/// Use this to customize instructions or agent list formatting for the background agents feature.
/// This property is ignored when <see cref="BackgroundAgents"/> is <see langword="null"/> or empty.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public BackgroundAgentsProviderOptions? BackgroundAgentsProviderOptions { get; set; }

#if NET
/// <summary>
/// Gets or sets the shell executor used to enable shell tool and environment probing via <see cref="ShellEnvironmentProvider"/>.
/// </summary>
/// <remarks>
/// When non-null, a <see cref="ShellEnvironmentProvider"/> is automatically included in the agent's context
/// providers (injecting OS/shell/CWD information into the system prompt), and the executor's
/// <see cref="ShellExecutor.AsAIFunction"/> is registered as a callable tool.
/// When <see langword="null"/> (the default), no shell features are enabled.
/// </remarks>
public ShellExecutor? ShellExecutor { get; set; }

/// <summary>
/// Gets or sets the name of the shell execution tool exposed to the model.
/// </summary>
/// <remarks>
/// <para>
/// When <see langword="null"/> (the default), the shell executor's default tool name (<c>run_shell</c>) is used.
/// This property is ignored when <see cref="ShellExecutor"/> is <see langword="null"/>.
/// </para>
/// <para>
/// <b>Security warning:</b> auto-approval rules may match tool calls solely by name. Pay attention to
/// the tool names approved by auto-approval rules for other features. Setting this property to a
/// value that collides with a tool name that is approved by an auto-approval rule for another feature will cause
/// the shell tool to also be auto-approved, bypassing the human approval boundary. Choose a unique
/// name that no other registered tool uses.
/// </para>
/// </remarks>
public string? ShellToolName { get; set; }

/// <summary>
/// Gets or sets the description of the shell execution tool shown to the model.
/// </summary>
/// <remarks>
/// When <see langword="null"/> (the default), the shell executor's built-in description is used.
/// This property is ignored when <see cref="ShellExecutor"/> is <see langword="null"/>.
/// </remarks>
public string? ShellToolDescription { get; set; }

/// <summary>
/// Gets or sets a value indicating whether approval is disabled for the shell execution tool.
/// </summary>
/// <remarks>
/// <para>
/// When <see langword="false"/> (the default), the shell tool is wrapped in an <see cref="ApprovalRequiredAIFunction"/>
/// so every command requires explicit approval before executing. When <see langword="true"/>, the tool can be invoked
/// without approval. This property is ignored when <see cref="ShellExecutor"/> is <see langword="null"/>.
/// </para>
/// <para>
/// Setting this to <see langword="true"/> also requires the underlying <see cref="ShellExecutor"/> to permit
/// unapproved use. The inverse of this value is forwarded as the <c>requireApproval</c> argument to
/// <see cref="ShellExecutor.AsAIFunction"/>, and some executors enforce their own security boundary:
/// <see cref="LocalShellExecutor"/> throws an <see cref="System.InvalidOperationException"/> unless it was
/// constructed with <see cref="LocalShellExecutorOptions.AcknowledgeUnsafe"/> set to <see langword="true"/>,
/// because running unapproved commands directly on the host is inherently unsafe. Sandboxed executors such as
/// <see cref="DockerShellExecutor"/> impose no such requirement.
/// </para>
/// </remarks>
public bool DisableShellToolApproval { get; set; }

/// <summary>
/// Gets or sets optional configuration for the <see cref="ShellEnvironmentProvider"/>.
/// </summary>
/// <remarks>
/// Use this to customize which tools are probed, the probe timeout, shell family override,
/// or the instructions formatter.
/// This property is ignored when <see cref="ShellExecutor"/> is <see langword="null"/>.
/// </remarks>
public ShellEnvironmentProviderOptions? ShellEnvironmentProviderOptions { get; set; }
#endif
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<IsReleaseCandidate>false</IsReleaseCandidate>
<IsReleased>true</IsReleased>
<InjectSharedThrow>true</InjectSharedThrow>
<InjectSharedDiagnosticIds>true</InjectSharedDiagnosticIds>
<InjectExperimentalAttributeOnLegacy>true</InjectExperimentalAttributeOnLegacy>
<InjectIsExternalInitOnLegacy>true</InjectIsExternalInitOnLegacy>
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
<NoWarn>$(NoWarn);MAAI001</NoWarn>
</PropertyGroup>

<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />

<!-- Disable package validation baseline until the first release -->
<PropertyGroup>
<PackageValidationBaselineVersion />
<EnablePackageValidation>false</EnablePackageValidation>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
</ItemGroup>

<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
<ProjectReference Include="..\Microsoft.Agents.AI.Tools.Shell\Microsoft.Agents.AI.Tools.Shell.csproj" />
</ItemGroup>

<PropertyGroup>
<!-- NuGet Package Settings -->
<Title>Microsoft Agent Framework Harness</Title>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ public FileAccessProvider(AgentFileStore fileStore, FileAccessProviderOptions? o
/// </para>
/// <para>
/// <b>Security note:</b> because matching is by tool name only, any other registered tool that
/// shares one of these names — for example a configurable-name tool such as the Harness shell
/// tool (<c>HarnessAgentOptions.ShellToolName</c>) that was assigned the same name — will also be auto-approved, bypassing the
/// shares one of these names — for example a configurable-name tool that was assigned the same
/// name — will also be auto-approved, bypassing the
/// human approval boundary. Ensure no other tool collides with these reserved names.
/// </para>
/// </remarks>
Expand Down Expand Up @@ -221,8 +221,8 @@ public FileAccessProvider(AgentFileStore fileStore, FileAccessProviderOptions? o
/// </para>
/// <para>
/// <b>Security note:</b> because matching is by tool name only, any other registered tool that
/// shares one of these names — for example a configurable-name tool such as the Harness shell
/// tool (<c>HarnessAgentOptions.ShellToolName</c>) that was assigned the same name — will also be auto-approved, bypassing the
/// shares one of these names — for example a configurable-name tool that was assigned the same
/// name — will also be auto-approved, bypassing the
/// human approval boundary. Ensure no other tool collides with these reserved names.
/// </para>
/// </remarks>
Expand Down
8 changes: 4 additions & 4 deletions dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ public sealed partial class AgentSkillsProvider : AIContextProvider, IDisposable
/// </para>
/// <para>
/// <b>Security note:</b> because matching is by tool name only, any other registered tool that
/// shares one of these names — for example a configurable-name tool such as the Harness shell
/// tool (<c>HarnessAgentOptions.ShellToolName</c>) that was assigned the same name — will also be auto-approved, bypassing the
/// shares one of these names — for example a configurable-name tool that was assigned the same
/// name — will also be auto-approved, bypassing the
/// human approval boundary. Ensure no other tool collides with these reserved names.
/// </para>
/// </remarks>
Expand Down Expand Up @@ -133,8 +133,8 @@ public sealed partial class AgentSkillsProvider : AIContextProvider, IDisposable
/// </para>
/// <para>
/// <b>Security note:</b> because matching is by tool name only, any other registered tool that
/// shares one of these names — for example a configurable-name tool such as the Harness shell
/// tool (<c>HarnessAgentOptions.ShellToolName</c>) that was assigned the same name — will also be auto-approved, bypassing the
/// shares one of these names — for example a configurable-name tool that was assigned the same
/// name — will also be auto-approved, bypassing the
/// human approval boundary. Ensure no other tool collides with these reserved names.
/// </para>
/// </remarks>
Expand Down
Loading
Loading