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 f2fcfa52daf..fda733d635d 100644 --- a/dotnet/samples/02-agents/Harness/Harness_Step01_Research/Program.cs +++ b/dotnet/samples/02-agents/Harness/Harness_Step01_Research/Program.cs @@ -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")), 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 aa2ad5c7d12..daaa61a9a19 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 @@ -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 { @@ -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], 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 00f50c17b37..78a41fd3ed4 100644 --- a/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/Program.cs +++ b/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/Program.cs @@ -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: diff --git a/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/README.md b/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/README.md index 2eddb0cadba..870d3d78ee2 100644 --- a/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/README.md +++ b/dotnet/samples/02-agents/Harness/Harness_Step03_DataProcessing/README.md @@ -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 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 46f4c32c81e..b074089d198 100644 --- a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs +++ b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs @@ -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), @@ -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 diff --git a/dotnet/samples/02-agents/Harness/Harness_Step05_Loop/Program.cs b/dotnet/samples/02-agents/Harness/Harness_Step05_Loop/Program.cs index 5236302a451..98b18172fad 100644 --- a/dotnet/samples/02-agents/Harness/Harness_Step05_Loop/Program.cs +++ b/dotnet/samples/02-agents/Harness/Harness_Step05_Loop/Program.cs @@ -252,7 +252,6 @@ AIAgent CreateLeanHarnessAgent( DisableAgentModeProvider = true, DisableTodoProvider = disableTodoProvider, DisableFileMemory = true, - DisableFileAccess = true, DisableWebSearch = true, ToolApprovalAgentOptions = toolApprovalAgentOptions, ChatOptions = new ChatOptions diff --git a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs index 9bb00ce88ca..87fa7d47b59 100644 --- a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgent.cs @@ -43,13 +43,13 @@ namespace Microsoft.Agents.AI; /// — 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 . /// /// /// /// Optional context providers (enabled via ): /// +/// — shared file access providing read/write tools for a working directory. Enable by setting ; configure via . /// — 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). /// @@ -320,13 +320,9 @@ private static List 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) diff --git a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs index 9d47be27866..be61687088a 100644 --- a/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Harness/HarnessAgentOptions.cs @@ -237,24 +237,24 @@ public sealed class HarnessAgentOptions public AgentFileStore? FileMemoryStore { get; set; } /// - /// Gets or sets a value indicating whether the is disabled. + /// Gets or sets the that enables the . /// /// - /// When (the default), a is included in the - /// agent's context providers, using either or a default - /// rooted at {cwd}/working. + /// File access is opt-in. When (the default), no + /// is added and the agent has no file access tools. When set, a is + /// included in the agent's context providers, backed by the supplied store and configured with + /// when provided. /// - public bool DisableFileAccess { get; set; } + public AgentFileStore? FileAccessStore { get; set; } /// - /// Gets or sets a custom for the . + /// Gets or sets the used to configure the . /// /// - /// When and is , - /// a default is created. - /// This property is ignored when is . + /// This property is only used when is set (file access is opt-in). + /// When , the provider uses its default options. /// - public AgentFileStore? FileAccessStore { get; set; } + public FileAccessProviderOptions? FileAccessProviderOptions { get; set; } /// /// Gets or sets a value indicating whether the is disabled. diff --git a/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentOptionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentOptionsTests.cs index 21e158138af..dc54a92f0c3 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentOptionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentOptionsTests.cs @@ -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); @@ -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); @@ -63,6 +63,7 @@ public void PropertiesCanBeSetAndRetrieved() var contextProviders = new AIContextProvider[] { new TodoProvider() }; var fileMemoryStore = new Mock().Object; var fileAccessStore = new Mock().Object; + var fileAccessOptions = new FileAccessProviderOptions(); var agentModeOptions = new AgentModeProviderOptions(); var skillsSource = new Mock().Object; var backgroundAgents = new AIAgent[] { new Mock().Object }; @@ -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, @@ -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); diff --git a/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs b/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs index cf64bfb711c..2b9613d0610 100644 --- a/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.Harness.UnitTests/HarnessAgentTests.cs @@ -32,7 +32,6 @@ public class HarnessAgentTests DisableToolAutoApproval = true, DisableOpenTelemetry = true, DisableFileMemory = true, - DisableFileAccess = true, DisableWebSearch = true, DisableTodoProvider = true, DisableAgentModeProvider = true, @@ -1212,15 +1211,15 @@ public void FileMemoryProvider_UsesCustomStore() #region Feature: FileAccessProvider /// - /// Verify that FileAccessProvider is included in AIContextProviders by default. + /// Verify that FileAccessProvider is included in AIContextProviders when a FileAccessStore is provided. /// [Fact] - public void FileAccessProvider_IncludedByDefault() + public void FileAccessProvider_IncludedWhenStoreProvided() { // Arrange var chatClient = new Mock().Object; var options = CreateAllDisabledOptions(); - options.DisableFileAccess = false; + options.FileAccessStore = new Mock().Object; // Act var agent = new HarnessAgent(chatClient, options); @@ -1232,10 +1231,10 @@ public void FileAccessProvider_IncludedByDefault() } /// - /// Verify that FileAccessProvider is excluded when disabled. + /// Verify that FileAccessProvider is excluded by default (opt-in: no store provided). /// [Fact] - public void FileAccessProvider_ExcludedWhenDisabled() + public void FileAccessProvider_ExcludedByDefault() { // Arrange var chatClient = new Mock().Object; @@ -1262,7 +1261,6 @@ public void FileAccessProvider_UsesCustomStore() var chatClient = new Mock().Object; var customStore = new Mock().Object; var options = CreateAllDisabledOptions(); - options.DisableFileAccess = false; options.FileAccessStore = customStore; // Act @@ -1274,6 +1272,46 @@ public void FileAccessProvider_UsesCustomStore() Assert.Contains(innerAgent!.AIContextProviders!, p => p is FileAccessProvider); } + /// + /// 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. + /// + [Fact] + public async Task FileAccessProvider_UsesProvidedOptionsAsync() + { + // Arrange + var chatClient = new Mock().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(); + + // Assert — the FileAccessProvider is present and honors the supplied options. + Assert.NotNull(innerAgent?.AIContextProviders); + var fileAccessProvider = Assert.IsType( + Assert.Single(innerAgent!.AIContextProviders!, p => p is FileAccessProvider)); + + var mockAgent = new Mock().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().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 @@ -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 @@ -1903,7 +1940,6 @@ public void Constructor_LoggerFactoryIsUsedByDownstreamComponents() DisableToolAutoApproval = true, DisableOpenTelemetry = true, DisableFileMemory = true, - DisableFileAccess = true, DisableWebSearch = true, DisableTodoProvider = true, DisableAgentModeProvider = true, @@ -1954,7 +1990,6 @@ public void Constructor_SucceedsWithoutTokenValues() DisableToolAutoApproval = true, DisableOpenTelemetry = true, DisableFileMemory = true, - DisableFileAccess = true, DisableWebSearch = true, DisableTodoProvider = true, DisableAgentModeProvider = true, @@ -1986,7 +2021,6 @@ public void Constructor_SucceedsWithOnlyMaxContextWindowTokens() DisableToolAutoApproval = true, DisableOpenTelemetry = true, DisableFileMemory = true, - DisableFileAccess = true, DisableWebSearch = true, DisableTodoProvider = true, DisableAgentModeProvider = true,