Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -85,7 +85,6 @@ Track your sources — you will need them when presenting results.
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
OpenTelemetrySourceName = TracingSourceName, // Use our custom source name so spans are captured by the TracerProvider above.
FileMemoryStore = new FileSystemAgentFileStore( // Configure the file memory provider to store files in a local folder called "agent-files".
Path.Combine(AppContext.BaseDirectory, "agent-files")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
DisableTodoProvider = true,
DisableAgentModeProvider = true,
DisableFileMemory = true, // If enabled, this would allow the agent to store memories as files in a directory associated with the current session
DisableFileAccess = true, // If enabled, this would allow the agent to read/write files in a working directory
DisableToolAutoApproval = true, // If true, this disables the don't-ask-again approval functionality.
ChatOptions = new ChatOptions
{
Expand Down Expand Up @@ -107,7 +106,6 @@ 5. Clear all completed tasks to free memory.
DisableTodoProvider = true,
DisableAgentModeProvider = true,
DisableFileMemory = true, // If enabled, this would allow the agent to store memories as files in a directory associated with the current session
DisableFileAccess = true, // If enabled, this would allow the agent to read/write files in a working directory
DisableToolAutoApproval = true, // If true, this disables the don't-ask-again approval functionality.
DisableWebSearch = true,
BackgroundAgents = [webSearchAgent],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.

// This sample demonstrates how to use a HarnessAgent with the default FileAccessProvider
// This sample demonstrates how to use a HarnessAgent with the FileAccessProvider
// to give an agent access to a folder of CSV data files. The agent can read, analyze,
// and extract information from the data, then write results back as new files.
//
// The sample includes a pre-populated `working/` folder with sales transaction data.
// The HarnessAgent's default FileAccessProvider uses `{cwd}/working` as its working directory,
// which matches this sample's folder layout.
// File access is opt-in: setting HarnessAgentOptions.FileAccessStore enables the
// FileAccessProvider, and this sample points it at the `working/` folder below the location of the executable.
// Ask the agent to analyze the data, produce summaries, or create new output files.
//
// Special commands:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# What this sample demonstrates

This sample demonstrates how to use a `HarnessAgent` with the default `FileAccessProvider` to give an agent access to a folder of data files for reading, analyzing, and writing results. The `HarnessAgent` pre-configures function invocation, per-service-call chat history persistence, in-loop compaction, tool approval, and OpenTelemetry — so the sample only needs to supply the chat client, token limits, custom instructions, and opt out of unused features.
This sample demonstrates how to use a `HarnessAgent` with the `FileAccessProvider` to give an agent access to a folder of data files for reading, analyzing, and writing results. The `HarnessAgent` pre-configures function invocation, per-service-call chat history persistence, in-loop compaction, tool approval, and OpenTelemetry — so the sample only needs to supply the chat client, token limits, custom instructions, a `FileAccessStore`, and opt out of unused features.

Key features showcased:

- **HarnessAgent** — a pre-configured agent that wraps a `ChatClientAgent` with function invocation, per-service-call persistence, and context-window compaction
- **FileAccessProvider** — the HarnessAgent's default file access provider uses `{cwd}/working` as its working directory, matching this sample's `working/` folder
- **FileAccessProvider** — file access is opt-in; setting `HarnessAgentOptions.FileAccessStore` to the sample's `working/` folder enables the provider's read/write tools
- **CSV data processing** — the agent reads sales transaction data and performs analysis on demand
- **Output file creation** — the agent can write summaries, filtered data, or reports back to the data folder
- **Streaming output** — responses are streamed token-by-token for a natural experience
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ to solve problems precisely rather than guessing. You also have access to skills
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
// Create the agent with ALL HarnessAgent features enabled plus Hyperlight CodeAct.
// No Disable* flags are set — TodoProvider, AgentModeProvider, FileMemory, FileAccess,
// ToolApproval, WebSearch, and AgentSkillsProvider are all active.
// TodoProvider, AgentModeProvider, FileMemory, ToolApproval, WebSearch, and
// AgentSkillsProvider are on by default. File access is opt-in, so it is enabled here by
// supplying a FileAccessStore.
AIAgent agent =
new AIProjectClient(
new Uri(endpoint),
Expand All @@ -101,6 +102,8 @@ to solve problems precisely rather than guessing. You also have access to skills
OpenTelemetrySourceName = TracingSourceName,
// Point the file memory at a local folder for persistent memory across sessions.
FileMemoryStore = new FileSystemAgentFileStore(Path.Combine(AppContext.BaseDirectory, "agent-files")),
// Enable file access (opt-in) by rooting the file access tools at a local working folder.
FileAccessStore = new FileSystemAgentFileStore(Path.Combine(AppContext.BaseDirectory, "working")),
// Add the HyperlightCodeActProvider so the agent can execute Python code in a sandbox.
AIContextProviders = [codeAct],
ChatOptions = new ChatOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ AIAgent CreateLeanHarnessAgent(
DisableAgentModeProvider = true,
DisableTodoProvider = disableTodoProvider,
DisableFileMemory = true,
DisableFileAccess = true,
DisableWebSearch = true,
ToolApprovalAgentOptions = toolApprovalAgentOptions,
ChatOptions = new ChatOptions
Expand Down
10 changes: 3 additions & 7 deletions dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ namespace Microsoft.Agents.AI;
/// <item><description><see cref="TodoProvider"/> — persistent todo list that the agent uses to track multi-step plans. Disable with <see cref="HarnessAgentOptions.DisableTodoProvider"/>.</description></item>
/// <item><description><see cref="AgentModeProvider"/> — mode tracking (e.g., "plan" vs "execute") that the agent uses to structure its work. Disable with <see cref="HarnessAgentOptions.DisableAgentModeProvider"/>.</description></item>
/// <item><description><see cref="FileMemoryProvider"/> — file-based session memory allowing the agent to persist notes and artifacts across turns. Disable with <see cref="HarnessAgentOptions.DisableFileMemory"/>.</description></item>
/// <item><description><see cref="FileAccessProvider"/> — shared file access providing read/write tools for a working directory. Disable with <see cref="HarnessAgentOptions.DisableFileAccess"/>.</description></item>
/// <item><description><see cref="AgentSkillsProvider"/> — discovers and loads skill definitions from the file system, enabling dynamic tool sets. Disable with <see cref="HarnessAgentOptions.DisableAgentSkillsProvider"/>.</description></item>
/// </list>
/// </para>
/// <para>
/// <strong>Optional context providers (enabled via <see cref="HarnessAgentOptions"/>):</strong>
/// <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>
Expand Down Expand Up @@ -320,13 +320,9 @@ private static List<AIContextProvider> BuildContextProviders(HarnessAgentOptions
}));
}

if (options?.DisableFileAccess is not true)
if (options?.FileAccessStore is AgentFileStore fileAccessStore)
{
AgentFileStore fileAccessStore = options?.FileAccessStore
?? new FileSystemAgentFileStore(
Path.Combine(Directory.GetCurrentDirectory(), "working"));

providers.Add(new FileAccessProvider(fileAccessStore));
providers.Add(new FileAccessProvider(fileAccessStore, options.FileAccessProviderOptions));
}

if (options?.DisableAgentSkillsProvider is not true)
Expand Down
20 changes: 10 additions & 10 deletions dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,24 +237,24 @@ public sealed class HarnessAgentOptions
public AgentFileStore? FileMemoryStore { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the <see cref="FileAccessProvider"/> is disabled.
/// Gets or sets the <see cref="AgentFileStore"/> that enables the <see cref="FileAccessProvider"/>.
/// </summary>
/// <remarks>
/// When <see langword="false"/> (the default), a <see cref="FileAccessProvider"/> is included in the
/// agent's context providers, using either <see cref="FileAccessStore"/> or a default
/// <see cref="FileSystemAgentFileStore"/> rooted at <c>{cwd}/working</c>.
/// File access is opt-in. When <see langword="null"/> (the default), no <see cref="FileAccessProvider"/>
/// is added and the agent has no file access tools. When set, a <see cref="FileAccessProvider"/> is
/// included in the agent's context providers, backed by the supplied store and configured with
/// <see cref="FileAccessProviderOptions"/> when provided.
/// </remarks>
public bool DisableFileAccess { get; set; }
public AgentFileStore? FileAccessStore { get; set; }

/// <summary>
/// Gets or sets a custom <see cref="AgentFileStore"/> for the <see cref="FileAccessProvider"/>.
/// Gets or sets the <see cref="FileAccessProviderOptions"/> used to configure the <see cref="FileAccessProvider"/>.
/// </summary>
/// <remarks>
/// When <see langword="null"/> and <see cref="DisableFileAccess"/> is <see langword="false"/>,
/// a default <see cref="FileSystemAgentFileStore"/> is created.
/// This property is ignored when <see cref="DisableFileAccess"/> is <see langword="true"/>.
/// 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>
public AgentFileStore? FileAccessStore { get; set; }
public FileAccessProviderOptions? FileAccessProviderOptions { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the <see cref="HostedWebSearchTool"/> is disabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public void DefaultPropertyValues()
Assert.False(options.DisableToolAutoApproval);
Assert.False(options.DisableApprovalNotRequiredFunctionBypassing);
Assert.False(options.DisableFileMemory);
Assert.False(options.DisableFileAccess);
Assert.False(options.DisableWebSearch);
Assert.False(options.DisableTodoProvider);
Assert.False(options.DisableAgentModeProvider);
Expand All @@ -42,6 +41,7 @@ public void DefaultPropertyValues()
Assert.Null(options.MaximumIterationsPerRequest);
Assert.Null(options.FileMemoryStore);
Assert.Null(options.FileAccessStore);
Assert.Null(options.FileAccessProviderOptions);
Assert.Null(options.AgentModeProviderOptions);
Assert.Null(options.AgentSkillsSource);
Assert.Null(options.BackgroundAgents);
Expand All @@ -63,6 +63,7 @@ public void PropertiesCanBeSetAndRetrieved()
var contextProviders = new AIContextProvider[] { new TodoProvider() };
var fileMemoryStore = new Mock<AgentFileStore>().Object;
var fileAccessStore = new Mock<AgentFileStore>().Object;
var fileAccessOptions = new FileAccessProviderOptions();
var agentModeOptions = new AgentModeProviderOptions();
var skillsSource = new Mock<AgentSkillsSource>().Object;
var backgroundAgents = new AIAgent[] { new Mock<AIAgent>().Object };
Expand All @@ -89,8 +90,8 @@ public void PropertiesCanBeSetAndRetrieved()
DisableApprovalNotRequiredFunctionBypassing = true,
DisableFileMemory = true,
FileMemoryStore = fileMemoryStore,
DisableFileAccess = true,
FileAccessStore = fileAccessStore,
FileAccessProviderOptions = fileAccessOptions,
DisableWebSearch = true,
DisableTodoProvider = true,
DisableAgentModeProvider = true,
Expand Down Expand Up @@ -124,8 +125,8 @@ public void PropertiesCanBeSetAndRetrieved()
Assert.True(options.DisableApprovalNotRequiredFunctionBypassing);
Assert.True(options.DisableFileMemory);
Assert.Same(fileMemoryStore, options.FileMemoryStore);
Assert.True(options.DisableFileAccess);
Assert.Same(fileAccessStore, options.FileAccessStore);
Assert.Same(fileAccessOptions, options.FileAccessProviderOptions);
Assert.True(options.DisableWebSearch);
Assert.True(options.DisableTodoProvider);
Assert.True(options.DisableAgentModeProvider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public class HarnessAgentTests
DisableToolAutoApproval = true,
DisableOpenTelemetry = true,
DisableFileMemory = true,
DisableFileAccess = true,
DisableWebSearch = true,
DisableTodoProvider = true,
DisableAgentModeProvider = true,
Expand Down Expand Up @@ -1212,15 +1211,15 @@ public void FileMemoryProvider_UsesCustomStore()
#region Feature: FileAccessProvider

/// <summary>
/// Verify that FileAccessProvider is included in AIContextProviders by default.
/// Verify that FileAccessProvider is included in AIContextProviders when a FileAccessStore is provided.
/// </summary>
[Fact]
public void FileAccessProvider_IncludedByDefault()
public void FileAccessProvider_IncludedWhenStoreProvided()
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
var options = CreateAllDisabledOptions();
options.DisableFileAccess = false;
options.FileAccessStore = new Mock<AgentFileStore>().Object;

// Act
var agent = new HarnessAgent(chatClient, options);
Expand All @@ -1232,10 +1231,10 @@ public void FileAccessProvider_IncludedByDefault()
}

/// <summary>
/// Verify that FileAccessProvider is excluded when disabled.
/// Verify that FileAccessProvider is excluded by default (opt-in: no store provided).
/// </summary>
[Fact]
public void FileAccessProvider_ExcludedWhenDisabled()
public void FileAccessProvider_ExcludedByDefault()
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
Expand All @@ -1262,7 +1261,6 @@ public void FileAccessProvider_UsesCustomStore()
var chatClient = new Mock<IChatClient>().Object;
var customStore = new Mock<AgentFileStore>().Object;
var options = CreateAllDisabledOptions();
options.DisableFileAccess = false;
options.FileAccessStore = customStore;

// Act
Expand All @@ -1274,6 +1272,46 @@ public void FileAccessProvider_UsesCustomStore()
Assert.Contains(innerAgent!.AIContextProviders!, p => p is FileAccessProvider);
}

/// <summary>
/// Verify that FileAccessProviderOptions are honored: setting DisableWriteTools must remove the
/// write tools from the provider that HarnessAgent wires up, while the read-only tools remain.
/// </summary>
[Fact]
public async Task FileAccessProvider_UsesProvidedOptionsAsync()
{
// Arrange
var chatClient = new Mock<IChatClient>().Object;
var options = CreateAllDisabledOptions();
options.FileAccessStore = new InMemoryAgentFileStore();
options.FileAccessProviderOptions = new FileAccessProviderOptions { DisableWriteTools = true };

// Act
var agent = new HarnessAgent(chatClient, options);
var innerAgent = agent.GetService<ChatClientAgent>();

// Assert — the FileAccessProvider is present and honors the supplied options.
Assert.NotNull(innerAgent?.AIContextProviders);
var fileAccessProvider = Assert.IsType<FileAccessProvider>(
Assert.Single(innerAgent!.AIContextProviders!, p => p is FileAccessProvider));

var mockAgent = new Mock<AIAgent>().Object;
var session = await agent.CreateSessionAsync();
#pragma warning disable MAAI001
var context = new AIContextProvider.InvokingContext(mockAgent, session, new AIContext());
#pragma warning restore MAAI001
AIContext result = await fileAccessProvider.InvokingAsync(context);
var toolNames = result.Tools!.OfType<AIFunction>().Select(t => t.Name).ToList();

// DisableWriteTools = true => only the read-only tools are exposed.
Assert.Contains(FileAccessProvider.ReadFileToolName, toolNames);
Assert.Contains(FileAccessProvider.LsToolName, toolNames);
Assert.Contains(FileAccessProvider.GrepToolName, toolNames);
Assert.DoesNotContain(FileAccessProvider.WriteToolName, toolNames);
Assert.DoesNotContain(FileAccessProvider.DeleteFileToolName, toolNames);
Assert.DoesNotContain(FileAccessProvider.ReplaceToolName, toolNames);
Assert.DoesNotContain(FileAccessProvider.ReplaceLinesToolName, toolNames);
}

#endregion

#region Feature: AgentSkillsProvider
Expand Down Expand Up @@ -1419,7 +1457,6 @@ public async Task AllDefaults_AllFeaturesEnabledAsync()
Assert.Contains(providers, p => p is TodoProvider);
Assert.Contains(providers, p => p is AgentModeProvider);
Assert.Contains(providers, p => p is FileMemoryProvider);
Assert.Contains(providers, p => p is FileAccessProvider);
Assert.Contains(providers, p => p is AgentSkillsProvider);

// Assert — HostedWebSearchTool is present in the tools sent to the model
Expand Down Expand Up @@ -1903,7 +1940,6 @@ public void Constructor_LoggerFactoryIsUsedByDownstreamComponents()
DisableToolAutoApproval = true,
DisableOpenTelemetry = true,
DisableFileMemory = true,
DisableFileAccess = true,
DisableWebSearch = true,
DisableTodoProvider = true,
DisableAgentModeProvider = true,
Expand Down Expand Up @@ -1954,7 +1990,6 @@ public void Constructor_SucceedsWithoutTokenValues()
DisableToolAutoApproval = true,
DisableOpenTelemetry = true,
DisableFileMemory = true,
DisableFileAccess = true,
DisableWebSearch = true,
DisableTodoProvider = true,
DisableAgentModeProvider = true,
Expand Down Expand Up @@ -1986,7 +2021,6 @@ public void Constructor_SucceedsWithOnlyMaxContextWindowTokens()
DisableToolAutoApproval = true,
DisableOpenTelemetry = true,
DisableFileMemory = true,
DisableFileAccess = true,
DisableWebSearch = true,
DisableTodoProvider = true,
DisableAgentModeProvider = true,
Expand Down
Loading