From c0b88f2864cb3b0aa942128603b9cf151849af78 Mon Sep 17 00:00:00 2001
From: SergeyMenshykh <68852919+SergeyMenshykh@users.noreply.github.com>
Date: Mon, 2 Mar 2026 10:41:19 +0000
Subject: [PATCH] Revert ".NET: Support hosted code interpreter for skill
script execution (#4192)"
This reverts commit c9cd067be6b2981791a9d93b1c832390a39b507a.
---
dotnet/agent-framework-dotnet.slnx | 1 -
.../Agent_Step01_BasicSkills/Program.cs | 3 -
..._ScriptExecutionWithCodeInterpreter.csproj | 28 ---
.../Program.cs | 49 -----
.../README.md | 72 --------
.../skills/password-generator/SKILL.md | 16 --
.../references/PASSWORD_GUIDELINES.md | 24 ---
.../password-generator/scripts/generate.py | 11 --
.../samples/02-agents/AgentSkills/README.md | 1 -
.../Skills/FileAgentSkill.cs | 21 +--
.../Skills/FileAgentSkillLoader.cs | 22 +--
.../FileAgentSkillScriptExecutionContext.cs | 35 ----
.../FileAgentSkillScriptExecutionDetails.cs | 25 ---
.../Skills/FileAgentSkillScriptExecutor.cs | 42 -----
.../Skills/FileAgentSkillsProvider.cs | 49 ++---
.../Skills/FileAgentSkillsProviderOptions.cs | 14 +-
...InterpreterFileAgentSkillScriptExecutor.cs | 35 ----
...killFrontmatter.cs => SkillFrontmatter.cs} | 9 +-
.../AgentSkills/FileAgentSkillLoaderTests.cs | 50 +-----
.../FileAgentSkillScriptExecutorTests.cs | 170 ------------------
.../FileAgentSkillsProviderTests.cs | 17 +-
...preterFileAgentSkillScriptExecutorTests.cs | 72 --------
22 files changed, 65 insertions(+), 701 deletions(-)
delete mode 100644 dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Agent_Step02_ScriptExecutionWithCodeInterpreter.csproj
delete mode 100644 dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Program.cs
delete mode 100644 dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/README.md
delete mode 100644 dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/SKILL.md
delete mode 100644 dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/references/PASSWORD_GUIDELINES.md
delete mode 100644 dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/scripts/generate.py
delete mode 100644 dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionContext.cs
delete mode 100644 dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionDetails.cs
delete mode 100644 dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutor.cs
delete mode 100644 dotnet/src/Microsoft.Agents.AI/Skills/HostedCodeInterpreterFileAgentSkillScriptExecutor.cs
rename dotnet/src/Microsoft.Agents.AI/Skills/{FileAgentSkillFrontmatter.cs => SkillFrontmatter.cs} (70%)
delete mode 100644 dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillScriptExecutorTests.cs
delete mode 100644 dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/HostedCodeInterpreterFileAgentSkillScriptExecutorTests.cs
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 1ab73c2b8b..b96b891b00 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -82,7 +82,6 @@
-
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs
index eef57e840a..290c3f9b6b 100644
--- a/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs
+++ b/dotnet/samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Program.cs
@@ -22,9 +22,6 @@
var skillsProvider = new FileAgentSkillsProvider(skillPath: Path.Combine(AppContext.BaseDirectory, "skills"));
// --- Agent Setup ---
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
.GetResponsesClient(deploymentName)
.AsAIAgent(new ChatClientAgentOptions
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Agent_Step02_ScriptExecutionWithCodeInterpreter.csproj b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Agent_Step02_ScriptExecutionWithCodeInterpreter.csproj
deleted file mode 100644
index 2a503bbfb2..0000000000
--- a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Agent_Step02_ScriptExecutionWithCodeInterpreter.csproj
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
- Exe
- net10.0
-
- enable
- enable
- $(NoWarn);MAAI001
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
-
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Program.cs b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Program.cs
deleted file mode 100644
index 2835ec70ab..0000000000
--- a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Program.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-// This sample demonstrates how to use Agent Skills with script execution via the hosted code interpreter.
-// When FileAgentSkillScriptExecutor.HostedCodeInterpreter() is configured, the agent can load and execute scripts
-// from skill resources using the LLM provider's built-in code interpreter.
-//
-// This sample includes the password-generator skill:
-// - A Python script for generating secure passwords
-
-using Azure.AI.OpenAI;
-using Azure.Identity;
-using Microsoft.Agents.AI;
-using OpenAI.Responses;
-
-// --- Configuration ---
-string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
- ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
-string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";
-
-// --- Skills Provider with Script Execution ---
-// Discovers skills and enables script execution via the hosted code interpreter
-var skillsProvider = new FileAgentSkillsProvider(
- skillPath: Path.Combine(AppContext.BaseDirectory, "skills"),
- options: new FileAgentSkillsProviderOptions
- {
- ScriptExecutor = FileAgentSkillScriptExecutor.HostedCodeInterpreter()
- });
-
-// --- Agent Setup ---
-// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
-// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
-// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
-AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
- .GetResponsesClient(deploymentName)
- .AsAIAgent(new ChatClientAgentOptions
- {
- Name = "SkillsAgent",
- ChatOptions = new()
- {
- Instructions = "You are a helpful assistant that can generate secure passwords.",
- },
- AIContextProviders = [skillsProvider],
- });
-
-// --- Example: Password generation with script execution ---
-Console.WriteLine("Example: Generating a password with a skill script");
-Console.WriteLine("---------------------------------------------------");
-AgentResponse response = await agent.RunAsync("Generate a secure password for my database account.");
-Console.WriteLine($"Agent: {response.Text}\n");
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/README.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/README.md
deleted file mode 100644
index f5bf63c44a..0000000000
--- a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/README.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# Script Execution with Code Interpreter
-
-This sample demonstrates how to use **Agent Skills** with **script execution** via the hosted code interpreter.
-
-## What's Different from Step01?
-
-In the [basic skills sample](../Agent_Step01_BasicSkills/), skills only provide instructions and resources as text. This sample adds **script execution** — the agent can load Python scripts from skill resources and execute them using the LLM provider's built-in code interpreter.
-
-This is enabled by configuring `FileAgentSkillScriptExecutor.HostedCodeInterpreter()` on the skills provider options:
-
-```csharp
-var skillsProvider = new FileAgentSkillsProvider(
- skillPath: Path.Combine(AppContext.BaseDirectory, "skills"),
- options: new FileAgentSkillsProviderOptions
- {
- ScriptExecutor = FileAgentSkillScriptExecutor.HostedCodeInterpreter()
- });
-```
-
-## Skills Included
-
-### password-generator
-Generates secure passwords using a Python script with configurable length and complexity.
-- `scripts/generate.py` — Password generation script
-- `references/PASSWORD_GUIDELINES.md` — Recommended length and symbol sets by use case
-
-## Project Structure
-
-```
-Agent_Step02_ScriptExecutionWithCodeInterpreter/
-├── Program.cs
-├── Agent_Step02_ScriptExecutionWithCodeInterpreter.csproj
-└── skills/
- └── password-generator/
- ├── SKILL.md
- ├── scripts/
- │ └── generate.py
- └── references/
- └── PASSWORD_GUIDELINES.md
-```
-
-## Running the Sample
-
-### Prerequisites
-- .NET 10.0 SDK
-- Azure OpenAI endpoint with a deployed model that supports code interpreter
-
-### Setup
-1. Set environment variables:
- ```bash
- export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"
- export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
- ```
-
-2. Run the sample:
- ```bash
- dotnet run
- ```
-
-### Example
-
-The sample asks the agent to generate a secure password. The agent:
-1. Loads the password-generator skill
-2. Reads the `generate.py` script via `read_skill_resource`
-3. Executes the script using the code interpreter with appropriate parameters
-4. Returns the generated password
-
-## Learn More
-
-- [Agent Skills Specification](https://agentskills.io/)
-- [Step01: Basic Skills](../Agent_Step01_BasicSkills/) — Skills without script execution
-- [Microsoft Agent Framework Documentation](../../../../../docs/)
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/SKILL.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/SKILL.md
deleted file mode 100644
index c3ef67401b..0000000000
--- a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/SKILL.md
+++ /dev/null
@@ -1,16 +0,0 @@
----
-name: password-generator
-description: Generate secure passwords using a Python script. Use when asked to create passwords or credentials.
----
-
-# Password Generator
-
-This skill generates secure passwords using a Python script.
-
-## Usage
-
-When the user requests a password:
-1. First, review `references/PASSWORD_GUIDELINES.md` to determine the recommended password length and character sets for the user's use case
-2. Load `scripts/generate.py` and adjust its parameters (length, character set) based on the guidelines and user's requirements
-3. Execute the script
-4. Present the generated password clearly
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/references/PASSWORD_GUIDELINES.md b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/references/PASSWORD_GUIDELINES.md
deleted file mode 100644
index be9145a4dd..0000000000
--- a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/references/PASSWORD_GUIDELINES.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# Password Generation Guidelines
-
-## General Rules
-
-- Never reuse passwords across services.
-- Always use cryptographically secure randomness (e.g., `random.SystemRandom()`).
-- Avoid dictionary words, keyboard patterns, and personal information.
-
-## Recommended Settings by Use Case
-
-| Use Case | Min Length | Character Set | Example |
-|-----------------------|-----------|----------------------------------------|--------------------------|
-| Web account | 16 | Upper + lower + digits + symbols | `G7!kQp@2xM#nW9$z` |
-| Database credential | 24 | Upper + lower + digits + symbols | `aR3$vK8!mN2@pQ7&xL5#wY` |
-| Wi-Fi / network key | 20 | Upper + lower + digits + symbols | `Ht4&jL9!rP2#mK7@xQ` |
-| API key / token | 32 | Upper + lower + digits (no symbols) | `k8Rm3xQ7nW2pL9vT4jH6yA` |
-| Encryption passphrase | 32 | Upper + lower + digits + symbols | `Xp4!kR8@mN2#vQ7&jL9$wT` |
-
-## Symbol Sets
-
-- **Standard symbols**: `!@#$%^&*()-_=+`
-- **Extended symbols**: `~`{}[]|;:'",.<>?/\`
-- **Safe symbols** (URL/shell-safe): `!@#$&*-_=+`
-- If the target system restricts symbols, use only the **safe** set.
diff --git a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/scripts/generate.py b/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/scripts/generate.py
deleted file mode 100644
index b44f3d9731..0000000000
--- a/dotnet/samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/skills/password-generator/scripts/generate.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# Password generator script
-# Usage: Adjust 'length' as needed, then run
-
-import random
-import string
-
-length = 16 # desired length
-
-pool = string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation
-password = "".join(random.SystemRandom().choice(pool) for _ in range(length))
-print(f"Generated password ({length} chars): {password}")
diff --git a/dotnet/samples/02-agents/AgentSkills/README.md b/dotnet/samples/02-agents/AgentSkills/README.md
index 477a738fb8..8488ec9eed 100644
--- a/dotnet/samples/02-agents/AgentSkills/README.md
+++ b/dotnet/samples/02-agents/AgentSkills/README.md
@@ -5,4 +5,3 @@ Samples demonstrating Agent Skills capabilities.
| Sample | Description |
|--------|-------------|
| [Agent_Step01_BasicSkills](Agent_Step01_BasicSkills/) | Using Agent Skills with a ChatClientAgent, including progressive disclosure and skill resources |
-| [Agent_Step02_ScriptExecutionWithCodeInterpreter](Agent_Step02_ScriptExecutionWithCodeInterpreter/) | Using Agent Skills with script execution via the hosted code interpreter |
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkill.cs b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkill.cs
index da0d0b83dd..f28bad3ab0 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkill.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkill.cs
@@ -1,8 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI;
@@ -15,8 +13,7 @@ namespace Microsoft.Agents.AI;
/// and a markdown body with instructions. Resource files referenced in the body are validated at
/// discovery time and read from disk on demand.
///
-[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
-public sealed class FileAgentSkill
+internal sealed class FileAgentSkill
{
///
/// Initializes a new instance of the class.
@@ -25,8 +22,8 @@ public sealed class FileAgentSkill
/// The SKILL.md content after the closing --- delimiter.
/// Absolute path to the directory containing this skill.
/// Relative paths of resource files referenced in the skill body.
- internal FileAgentSkill(
- FileAgentSkillFrontmatter frontmatter,
+ public FileAgentSkill(
+ SkillFrontmatter frontmatter,
string body,
string sourcePath,
IReadOnlyList? resourceNames = null)
@@ -40,20 +37,20 @@ internal FileAgentSkill(
///
/// Gets the parsed YAML frontmatter (name and description).
///
- public FileAgentSkillFrontmatter Frontmatter { get; }
+ public SkillFrontmatter Frontmatter { get; }
///
- /// Gets the directory path where the skill was discovered.
+ /// Gets the SKILL.md body content (without the YAML frontmatter).
///
- public string SourcePath { get; }
+ public string Body { get; }
///
- /// Gets the SKILL.md body content (without the YAML frontmatter).
+ /// Gets the directory path where the skill was discovered.
///
- internal string Body { get; }
+ public string SourcePath { get; }
///
/// Gets the relative paths of resource files referenced in the skill body (e.g., "references/FAQ.md").
///
- internal IReadOnlyList ResourceNames { get; }
+ public IReadOnlyList ResourceNames { get; }
}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillLoader.cs b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillLoader.cs
index 8f55fc93c3..8c034b3122 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillLoader.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillLoader.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
@@ -10,7 +9,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
-using Microsoft.Shared.DiagnosticIds;
namespace Microsoft.Agents.AI;
@@ -22,8 +20,7 @@ namespace Microsoft.Agents.AI;
/// Each file is validated for YAML frontmatter and resource integrity. Invalid skills are excluded
/// with logged warnings. Resource paths are checked against path traversal and symlink escape attacks.
///
-[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
-public sealed partial class FileAgentSkillLoader
+internal sealed partial class FileAgentSkillLoader
{
private const string SkillFileName = "SKILL.md";
private const int MaxSearchDepth = 2;
@@ -36,16 +33,13 @@ public sealed partial class FileAgentSkillLoader
// Example: "---\nname: foo\n---\nBody" → Group 1: "name: foo\n"
private static readonly Regex s_frontmatterRegex = new(@"\A\uFEFF?^---\s*$(.+?)^---\s*$", RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled, TimeSpan.FromSeconds(5));
- // Matches resource file references in skill markdown. Group 1 = relative file path.
- // Supports two forms:
- // 1. Markdown links: [text](path/file.ext)
- // 2. Backtick-quoted paths: `path/file.ext`
+ // Matches markdown links to local resource files. Group 1 = relative file path.
// Supports optional ./ or ../ prefixes; excludes URLs (no ":" in the path character class).
// Intentionally conservative: only matches paths with word characters, hyphens, dots,
// and forward slashes. Paths with spaces or special characters are not supported.
- // Examples: [doc](refs/FAQ.md) → "refs/FAQ.md", `./scripts/run.py` → "./scripts/run.py",
+ // Examples: [doc](refs/FAQ.md) → "refs/FAQ.md", [s](./s.json) → "./s.json",
// [p](../shared/doc.txt) → "../shared/doc.txt"
- private static readonly Regex s_resourceLinkRegex = new(@"(?:\[.*?\]\(|`)(\.?\.?/?[\w][\w\-./]*\.\w+)(?:\)|`)", RegexOptions.Compiled, TimeSpan.FromSeconds(5));
+ private static readonly Regex s_resourceLinkRegex = new(@"\[.*?\]\((\.?\.?/?[\w][\w\-./]*\.\w+)\)", RegexOptions.Compiled, TimeSpan.FromSeconds(5));
// Matches YAML "key: value" lines. Group 1 = key, Group 2 = quoted value, Group 3 = unquoted value.
// Accepts single or double quotes; the lazy quantifier trims trailing whitespace on unquoted values.
@@ -117,7 +111,7 @@ internal Dictionary DiscoverAndLoadSkills(IEnumerable
/// The resource is not registered, resolves outside the skill directory, or does not exist.
///
- public async Task ReadSkillResourceAsync(FileAgentSkill skill, string resourceName, CancellationToken cancellationToken = default)
+ internal async Task ReadSkillResourceAsync(FileAgentSkill skill, string resourceName, CancellationToken cancellationToken = default)
{
resourceName = NormalizeResourcePath(resourceName);
@@ -195,7 +189,7 @@ private static void SearchDirectoriesForSkills(string directory, List re
string content = File.ReadAllText(skillFilePath, Encoding.UTF8);
- if (!this.TryParseSkillDocument(content, skillFilePath, out FileAgentSkillFrontmatter frontmatter, out string body))
+ if (!this.TryParseSkillDocument(content, skillFilePath, out SkillFrontmatter frontmatter, out string body))
{
return null;
}
@@ -214,7 +208,7 @@ private static void SearchDirectoriesForSkills(string directory, List re
resourceNames: resourceNames);
}
- private bool TryParseSkillDocument(string content, string skillFilePath, out FileAgentSkillFrontmatter frontmatter, out string body)
+ private bool TryParseSkillDocument(string content, string skillFilePath, out SkillFrontmatter frontmatter, out string body)
{
frontmatter = null!;
body = null!;
@@ -270,7 +264,7 @@ private bool TryParseSkillDocument(string content, string skillFilePath, out Fil
return false;
}
- frontmatter = new FileAgentSkillFrontmatter(name, description);
+ frontmatter = new SkillFrontmatter(name, description);
body = content.Substring(match.Index + match.Length).TrimStart();
return true;
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionContext.cs b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionContext.cs
deleted file mode 100644
index c28333a715..0000000000
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionContext.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using Microsoft.Shared.DiagnosticIds;
-
-namespace Microsoft.Agents.AI;
-
-///
-/// Provides access to loaded skills and the skill loader for use by implementations.
-///
-[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
-public sealed class FileAgentSkillScriptExecutionContext
-{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The loaded skills dictionary.
- /// The skill loader for reading resources.
- internal FileAgentSkillScriptExecutionContext(Dictionary skills, FileAgentSkillLoader loader)
- {
- this.Skills = skills;
- this.Loader = loader;
- }
-
- ///
- /// Gets the loaded skills keyed by name.
- ///
- public IReadOnlyDictionary Skills { get; }
-
- ///
- /// Gets the skill loader for reading resources.
- ///
- public FileAgentSkillLoader Loader { get; }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionDetails.cs b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionDetails.cs
deleted file mode 100644
index 4c12848386..0000000000
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutionDetails.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using Microsoft.Extensions.AI;
-using Microsoft.Shared.DiagnosticIds;
-
-namespace Microsoft.Agents.AI;
-
-///
-/// Represents the tools and instructions contributed by a .
-///
-[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
-public sealed class FileAgentSkillScriptExecutionDetails
-{
- ///
- /// Gets the additional instructions to provide to the agent for script execution.
- ///
- public string? Instructions { get; set; }
-
- ///
- /// Gets the additional tools to provide to the agent for script execution.
- ///
- public IReadOnlyList? Tools { get; set; }
-}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutor.cs b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutor.cs
deleted file mode 100644
index 1171940e72..0000000000
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillScriptExecutor.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System.Diagnostics.CodeAnalysis;
-using Microsoft.Shared.DiagnosticIds;
-
-namespace Microsoft.Agents.AI;
-
-///
-/// Defines the contract for skill script execution modes.
-///
-///
-///
-/// A provides the instructions and tools needed to enable
-/// script execution within an agent skill. Concrete implementations determine how scripts
-/// are executed (e.g., via the LLM's hosted code interpreter, an external executor, or a hybrid approach).
-///
-///
-/// Use the static factory methods to create instances:
-///
-/// - — executes scripts using the LLM provider's built-in code interpreter.
-///
-///
-///
-[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
-public abstract class FileAgentSkillScriptExecutor
-{
- ///
- /// Creates a that uses the LLM provider's hosted code interpreter for script execution.
- ///
- /// A instance configured for hosted code interpreter execution.
- public static FileAgentSkillScriptExecutor HostedCodeInterpreter() => new HostedCodeInterpreterFileAgentSkillScriptExecutor();
-
- ///
- /// Returns the tools and instructions contributed by this executor.
- ///
- ///
- /// The execution context provided by the skills provider, containing the loaded skills
- /// and the skill loader for reading resources.
- ///
- /// A containing the executor's tools and instructions.
- protected internal abstract FileAgentSkillScriptExecutionDetails GetExecutionDetails(FileAgentSkillScriptExecutionContext context);
-}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProvider.cs b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProvider.cs
index 7acec160d4..847bf36a52 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProvider.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProvider.cs
@@ -48,21 +48,21 @@ You have access to skills containing domain-specific knowledge and capabilities.
Each skill provides specialized instructions, reference documents, and assets for specific tasks.
- {skills}
+ {0}
When a task aligns with a skill's domain:
- - Use `load_skill` to retrieve the skill's instructions
- - Follow the provided guidance
- - Use `read_skill_resource` to read any references or other files mentioned by the skill, always using the full path as written (e.g. `references/FAQ.md`, not just `FAQ.md`)
- {executor_instructions}
+ 1. Use `load_skill` to retrieve the skill's instructions
+ 2. Follow the provided guidance
+ 3. Use `read_skill_resource` to read any references or other files mentioned by the skill
+
Only load what is needed, when it is needed.
""";
private readonly Dictionary _skills;
private readonly ILogger _logger;
private readonly FileAgentSkillLoader _loader;
- private readonly IEnumerable _tools;
+ private readonly AITool[] _tools;
private readonly string? _skillsInstructionPrompt;
///
@@ -91,13 +91,9 @@ public FileAgentSkillsProvider(IEnumerable skillPaths, FileAgentSkillsPr
this._loader = new FileAgentSkillLoader(this._logger);
this._skills = this._loader.DiscoverAndLoadSkills(skillPaths);
- var executionDetails = options?.ScriptExecutor is { } executor
- ? executor.GetExecutionDetails(new(this._skills, this._loader))
- : null;
-
- this._skillsInstructionPrompt = BuildSkillsInstructionPrompt(options, this._skills, executionDetails?.Instructions);
+ this._skillsInstructionPrompt = BuildSkillsInstructionPrompt(options, this._skills);
- AITool[] baseTools =
+ this._tools =
[
AIFunctionFactory.Create(
this.LoadSkill,
@@ -108,10 +104,6 @@ public FileAgentSkillsProvider(IEnumerable skillPaths, FileAgentSkillsPr
name: "read_skill_resource",
description: "Reads a file associated with a skill, such as references or assets."),
];
-
- this._tools = executionDetails?.Tools is { Count: > 0 } executorTools
- ? baseTools.Concat(executorTools)
- : baseTools;
}
///
@@ -125,7 +117,7 @@ protected override ValueTask ProvideAIContextAsync(InvokingContext co
return new ValueTask(new AIContext
{
Instructions = this._skillsInstructionPrompt,
- Tools = this._tools,
+ Tools = this._tools
});
}
@@ -174,9 +166,24 @@ private async Task ReadSkillResourceAsync(string skillName, string resou
}
}
- private static string? BuildSkillsInstructionPrompt(FileAgentSkillsProviderOptions? options, Dictionary skills, string? instructions)
+ private static string? BuildSkillsInstructionPrompt(FileAgentSkillsProviderOptions? options, Dictionary skills)
{
- string promptTemplate = options?.SkillsInstructionPrompt ?? DefaultSkillsInstructionPrompt;
+ string promptTemplate = DefaultSkillsInstructionPrompt;
+
+ if (options?.SkillsInstructionPrompt is { } optionsInstructions)
+ {
+ try
+ {
+ promptTemplate = string.Format(optionsInstructions, string.Empty);
+ }
+ catch (FormatException ex)
+ {
+ throw new ArgumentException(
+ "The provided SkillsInstructionPrompt is not a valid format string. It must contain a '{0}' placeholder and escape any literal '{' or '}' by doubling them ('{{' or '}}').",
+ nameof(options),
+ ex);
+ }
+ }
if (skills.Count == 0)
{
@@ -195,9 +202,7 @@ private async Task ReadSkillResourceAsync(string skillName, string resou
sb.AppendLine(" ");
}
- return promptTemplate
- .Replace("{skills}", sb.ToString().TrimEnd())
- .Replace("{executor_instructions}", instructions ?? "\n");
+ return string.Format(promptTemplate, sb.ToString().TrimEnd());
}
[LoggerMessage(LogLevel.Information, "Loading skill: {SkillName}")]
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProviderOptions.cs
index 7d86d3b4ae..a47841c260 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProviderOptions.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillsProviderOptions.cs
@@ -13,20 +13,8 @@ public sealed class FileAgentSkillsProviderOptions
{
///
/// Gets or sets a custom system prompt template for advertising skills.
- /// Use {skills} as the placeholder for the generated skills list and
- /// {executor_instructions} for executor-provided instructions.
+ /// Use {0} as the placeholder for the generated skills list.
/// When , a default template is used.
///
public string? SkillsInstructionPrompt { get; set; }
-
- ///
- /// Gets or sets the skill executor that enables script execution for loaded skills.
- ///
- ///
- /// When (the default), script execution is disabled and skills only provide
- /// instructions and resources. Set this to a instance (e.g.,
- /// ) to enable script execution with
- /// mode-specific instructions and tools.
- ///
- public FileAgentSkillScriptExecutor? ScriptExecutor { get; set; }
}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/HostedCodeInterpreterFileAgentSkillScriptExecutor.cs b/dotnet/src/Microsoft.Agents.AI/Skills/HostedCodeInterpreterFileAgentSkillScriptExecutor.cs
deleted file mode 100644
index 88fb1f86a2..0000000000
--- a/dotnet/src/Microsoft.Agents.AI/Skills/HostedCodeInterpreterFileAgentSkillScriptExecutor.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using Microsoft.Extensions.AI;
-
-namespace Microsoft.Agents.AI;
-
-///
-/// A that uses the LLM provider's hosted code interpreter for script execution.
-///
-///
-/// This executor directs the LLM to load scripts via read_skill_resource and execute them
-/// using the provider's built-in code interpreter. A is
-/// registered to signal the provider to enable its code interpreter sandbox.
-///
-internal sealed class HostedCodeInterpreterFileAgentSkillScriptExecutor : FileAgentSkillScriptExecutor
-{
- private static readonly FileAgentSkillScriptExecutionDetails s_contribution = new()
- {
- Instructions =
- """
-
- Some skills include executable scripts (e.g., Python files) in their resources.
- When a skill's instructions reference a script:
- 1. Use `read_skill_resource` to load the script content
- 2. Execute the script using the code interpreter
-
- """,
- Tools = [new HostedCodeInterpreterTool()],
- };
-
- ///
-#pragma warning disable RCS1168 // Parameter name differs from base name
- protected internal override FileAgentSkillScriptExecutionDetails GetExecutionDetails(FileAgentSkillScriptExecutionContext _) => s_contribution;
-#pragma warning restore RCS1168 // Parameter name differs from base name
-}
diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillFrontmatter.cs b/dotnet/src/Microsoft.Agents.AI/Skills/SkillFrontmatter.cs
similarity index 70%
rename from dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillFrontmatter.cs
rename to dotnet/src/Microsoft.Agents.AI/Skills/SkillFrontmatter.cs
index c369ad319f..123a6c43f4 100644
--- a/dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillFrontmatter.cs
+++ b/dotnet/src/Microsoft.Agents.AI/Skills/SkillFrontmatter.cs
@@ -1,7 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
-using System.Diagnostics.CodeAnalysis;
-using Microsoft.Shared.DiagnosticIds;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI;
@@ -9,15 +7,14 @@ namespace Microsoft.Agents.AI;
///
/// Parsed YAML frontmatter from a SKILL.md file, containing the skill's name and description.
///
-[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
-public sealed class FileAgentSkillFrontmatter
+internal sealed class SkillFrontmatter
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// Skill name.
/// Skill description.
- internal FileAgentSkillFrontmatter(string name, string description)
+ public SkillFrontmatter(string name, string description)
{
this.Name = Throw.IfNullOrWhitespace(name);
this.Description = Throw.IfNullOrWhitespace(description);
diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs
index c9e154a277..c34eb6d7f2 100644
--- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillLoaderTests.cs
@@ -501,7 +501,7 @@ public async Task ReadSkillResourceAsync_SymlinkInPath_ThrowsInvalidOperationExc
}
// Manually construct a skill that bypasses discovery validation
- var frontmatter = new FileAgentSkillFrontmatter("symlink-read-skill", "A skill");
+ var frontmatter = new SkillFrontmatter("symlink-read-skill", "A skill");
var skill = new FileAgentSkill(
frontmatter: frontmatter,
body: "See [doc](refs/data.md).",
@@ -532,54 +532,6 @@ public void DiscoverAndLoadSkills_FileWithUtf8Bom_ParsesSuccessfully()
Assert.Equal("Body content.", skills["bom-skill"].Body);
}
- [Theory]
- [InlineData("No resource references.", new string[0])]
- [InlineData("Review `refs/FAQ.md` for details.", new[] { "refs/FAQ.md" })]
- [InlineData("See [guide](refs/guide.md) then run `scripts/run.py`.", new[] { "refs/guide.md", "scripts/run.py" })]
- public void DiscoverAndLoadSkills_ResourceReferences_ExtractsExpectedResourceNames(string body, string[] expectedResources)
- {
- // Arrange — create skill with resource files on disk so validation passes
- string skillDir = Path.Combine(this._testRoot, "res-skill");
- Directory.CreateDirectory(skillDir);
- foreach (string resource in expectedResources)
- {
- string resourcePath = Path.Combine(skillDir, resource.Replace('/', Path.DirectorySeparatorChar));
- Directory.CreateDirectory(Path.GetDirectoryName(resourcePath)!);
- File.WriteAllText(resourcePath, "content");
- }
-
- File.WriteAllText(
- Path.Combine(skillDir, "SKILL.md"),
- $"---\nname: res-skill\ndescription: Resource test\n---\n{body}");
-
- // Act
- var skills = this._loader.DiscoverAndLoadSkills(new[] { this._testRoot });
-
- // Assert
- Assert.Single(skills);
- var skill = skills["res-skill"];
- Assert.Equal(expectedResources.Length, skill.ResourceNames.Count);
- foreach (string expected in expectedResources)
- {
- Assert.Contains(expected, skill.ResourceNames);
- }
- }
-
- [Fact]
- public async Task ReadSkillResourceAsync_BacktickResourcePath_ReturnsContentAsync()
- {
- // Arrange — skill body uses backtick-quoted path
- _ = this.CreateSkillDirectoryWithResource("backtick-read", "A skill", "Load `refs/doc.md` first.", "refs/doc.md", "Backtick content.");
- var skills = this._loader.DiscoverAndLoadSkills(new[] { this._testRoot });
- var skill = skills["backtick-read"];
-
- // Act
- string content = await this._loader.ReadSkillResourceAsync(skill, "refs/doc.md");
-
- // Assert
- Assert.Equal("Backtick content.", content);
- }
-
private string CreateSkillDirectory(string name, string description, string body)
{
string skillDir = Path.Combine(this._testRoot, name);
diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillScriptExecutorTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillScriptExecutorTests.cs
deleted file mode 100644
index 1be56e49c9..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillScriptExecutorTests.cs
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace Microsoft.Agents.AI.UnitTests.AgentSkills;
-
-///
-/// Unit tests for and its integration with .
-///
-public sealed class FileAgentSkillScriptExecutorTests : IDisposable
-{
- private readonly string _testRoot;
- private readonly TestAIAgent _agent = new();
- private static readonly FileAgentSkillScriptExecutionContext s_emptyContext = new(
- new Dictionary(StringComparer.OrdinalIgnoreCase),
- new FileAgentSkillLoader(NullLogger.Instance));
-
- public FileAgentSkillScriptExecutorTests()
- {
- this._testRoot = Path.Combine(Path.GetTempPath(), "skill-executor-tests-" + Guid.NewGuid().ToString("N"));
- Directory.CreateDirectory(this._testRoot);
- }
-
- public void Dispose()
- {
- if (Directory.Exists(this._testRoot))
- {
- Directory.Delete(this._testRoot, recursive: true);
- }
- }
-
- [Fact]
- public void HostedCodeInterpreter_ReturnsNonNullInstance()
- {
- // Act
- var executor = FileAgentSkillScriptExecutor.HostedCodeInterpreter();
-
- // Assert
- Assert.NotNull(executor);
- }
-
- [Fact]
- public void HostedCodeInterpreter_GetExecutionDetails_ReturnsNonNullInstructions()
- {
- // Arrange
- var executor = FileAgentSkillScriptExecutor.HostedCodeInterpreter();
-
- // Act
- var details = executor.GetExecutionDetails(s_emptyContext);
-
- // Assert
- Assert.NotNull(details);
- Assert.NotNull(details.Instructions);
- Assert.NotEmpty(details.Instructions);
- }
-
- [Fact]
- public void HostedCodeInterpreter_GetExecutionDetails_ReturnsNonEmptyToolsList()
- {
- // Arrange
- var executor = FileAgentSkillScriptExecutor.HostedCodeInterpreter();
-
- // Act
- var details = executor.GetExecutionDetails(s_emptyContext);
-
- // Assert
- Assert.NotNull(details);
- Assert.NotNull(details.Tools);
- Assert.NotEmpty(details.Tools);
- }
-
- [Fact]
- public async Task Provider_WithExecutor_IncludesExecutorInstructionsInPromptAsync()
- {
- // Arrange
- CreateSkill(this._testRoot, "exec-skill", "Executor test", "Body.");
- var executor = FileAgentSkillScriptExecutor.HostedCodeInterpreter();
- var options = new FileAgentSkillsProviderOptions { ScriptExecutor = executor };
- var provider = new FileAgentSkillsProvider(this._testRoot, options);
- var invokingContext = new AIContextProvider.InvokingContext(this._agent, session: null, new AIContext());
-
- // Act
- var result = await provider.InvokingAsync(invokingContext, CancellationToken.None);
-
- // Assert — executor instructions should be merged into the prompt
- Assert.NotNull(result.Instructions);
- Assert.Contains("code interpreter", result.Instructions, StringComparison.OrdinalIgnoreCase);
- }
-
- [Fact]
- public async Task Provider_WithExecutor_IncludesExecutorToolsAsync()
- {
- // Arrange
- CreateSkill(this._testRoot, "tools-exec-skill", "Executor tools test", "Body.");
- var executor = FileAgentSkillScriptExecutor.HostedCodeInterpreter();
- var options = new FileAgentSkillsProviderOptions { ScriptExecutor = executor };
- var provider = new FileAgentSkillsProvider(this._testRoot, options);
- var invokingContext = new AIContextProvider.InvokingContext(this._agent, session: null, new AIContext());
-
- // Act
- var result = await provider.InvokingAsync(invokingContext, CancellationToken.None);
-
- // Assert — should have 3 tools: load_skill, read_skill_resource, and HostedCodeInterpreterTool
- Assert.NotNull(result.Tools);
- Assert.Equal(3, result.Tools!.Count());
- var toolNames = result.Tools!.Select(t => t.Name).ToList();
- Assert.Contains("load_skill", toolNames);
- Assert.Contains("read_skill_resource", toolNames);
- Assert.Single(result.Tools!, t => t is HostedCodeInterpreterTool);
- }
-
- [Fact]
- public async Task Provider_WithoutExecutor_DoesNotIncludeExecutorToolsAsync()
- {
- // Arrange
- CreateSkill(this._testRoot, "no-exec-skill", "No executor test", "Body.");
- var provider = new FileAgentSkillsProvider(this._testRoot);
- var invokingContext = new AIContextProvider.InvokingContext(this._agent, session: null, new AIContext());
-
- // Act
- var result = await provider.InvokingAsync(invokingContext, CancellationToken.None);
-
- // Assert — should only have the two base tools
- Assert.NotNull(result.Tools);
- Assert.Equal(2, result.Tools!.Count());
- }
-
- [Fact]
- public async Task Provider_WithHostedCodeInterpreter_MergesScriptInstructionsIntoPromptAsync()
- {
- // Arrange
- CreateSkill(this._testRoot, "merge-skill", "Merge test", "Body.");
- var executor = FileAgentSkillScriptExecutor.HostedCodeInterpreter();
- var options = new FileAgentSkillsProviderOptions { ScriptExecutor = executor };
- var provider = new FileAgentSkillsProvider(this._testRoot, options);
- var invokingContext = new AIContextProvider.InvokingContext(this._agent, session: null, new AIContext());
-
- // Act
- var result = await provider.InvokingAsync(invokingContext, CancellationToken.None);
-
- // Assert — prompt should contain both the skill listing and the executor's script instructions
- Assert.NotNull(result.Instructions);
- string instructions = result.Instructions!;
-
- // Skill listing is present
- Assert.Contains("merge-skill", instructions);
- Assert.Contains("Merge test", instructions);
-
- // Hosted code interpreter script instructions are merged into the prompt
- Assert.Contains("executable scripts", instructions);
- Assert.Contains("read_skill_resource", instructions);
- Assert.Contains("Execute the script using the code interpreter", instructions);
- }
-
- private static void CreateSkill(string root, string name, string description, string body)
- {
- string skillDir = Path.Combine(root, name);
- Directory.CreateDirectory(skillDir);
- File.WriteAllText(
- Path.Combine(skillDir, "SKILL.md"),
- $"---\nname: {name}\ndescription: {description}\n---\n{body}");
- }
-}
diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillsProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillsProviderTests.cs
index f95f3a7080..6bfaf1b546 100644
--- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillsProviderTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/FileAgentSkillsProviderTests.cs
@@ -96,7 +96,7 @@ public async Task InvokingCoreAsync_CustomPromptTemplate_UsesCustomTemplateAsync
this.CreateSkill("custom-prompt-skill", "Custom prompt", "Body.");
var options = new FileAgentSkillsProviderOptions
{
- SkillsInstructionPrompt = "Custom template: {skills}"
+ SkillsInstructionPrompt = "Custom template: {0}"
};
var provider = new FileAgentSkillsProvider(this._testRoot, options);
var inputContext = new AIContext();
@@ -110,6 +110,21 @@ public async Task InvokingCoreAsync_CustomPromptTemplate_UsesCustomTemplateAsync
Assert.StartsWith("Custom template:", result.Instructions);
}
+ [Fact]
+ public void Constructor_InvalidPromptTemplate_ThrowsArgumentException()
+ {
+ // Arrange — template with unescaped braces and no valid {0} placeholder
+ var options = new FileAgentSkillsProviderOptions
+ {
+ SkillsInstructionPrompt = "Bad template with {unescaped} braces"
+ };
+
+ // Act & Assert
+ var ex = Assert.Throws(() => new FileAgentSkillsProvider(this._testRoot, options));
+ Assert.Contains("SkillsInstructionPrompt", ex.Message);
+ Assert.Equal("options", ex.ParamName);
+ }
+
[Fact]
public async Task InvokingCoreAsync_SkillNamesAreXmlEscapedAsync()
{
diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/HostedCodeInterpreterFileAgentSkillScriptExecutorTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/HostedCodeInterpreterFileAgentSkillScriptExecutorTests.cs
deleted file mode 100644
index 84a4446779..0000000000
--- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/HostedCodeInterpreterFileAgentSkillScriptExecutorTests.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) Microsoft. All rights reserved.
-
-using System;
-using System.Collections.Generic;
-using Microsoft.Extensions.AI;
-using Microsoft.Extensions.Logging.Abstractions;
-
-namespace Microsoft.Agents.AI.UnitTests.AgentSkills;
-
-///
-/// Unit tests for .
-///
-public sealed class HostedCodeInterpreterFileAgentSkillScriptExecutorTests
-{
- private static readonly FileAgentSkillScriptExecutionContext s_emptyContext = new(
- new Dictionary(StringComparer.OrdinalIgnoreCase),
- new FileAgentSkillLoader(NullLogger.Instance));
-
- [Fact]
- public void GetExecutionDetails_ReturnsScriptExecutionGuidance()
- {
- // Arrange
- var executor = new HostedCodeInterpreterFileAgentSkillScriptExecutor();
-
- // Act
- var details = executor.GetExecutionDetails(s_emptyContext);
-
- // Assert
- Assert.NotNull(details.Instructions);
- Assert.Contains("read_skill_resource", details.Instructions);
- Assert.Contains("code interpreter", details.Instructions);
- }
-
- [Fact]
- public void GetExecutionDetails_ReturnsSingleHostedCodeInterpreterTool()
- {
- // Arrange
- var executor = new HostedCodeInterpreterFileAgentSkillScriptExecutor();
-
- // Act
- var details = executor.GetExecutionDetails(s_emptyContext);
-
- // Assert
- Assert.NotNull(details.Tools);
- Assert.Single(details.Tools!);
- Assert.IsType(details.Tools![0]);
- }
-
- [Fact]
- public void GetExecutionDetails_ReturnsSameInstanceOnMultipleCalls()
- {
- // Arrange
- var executor = new HostedCodeInterpreterFileAgentSkillScriptExecutor();
-
- // Act
- var details1 = executor.GetExecutionDetails(s_emptyContext);
- var details2 = executor.GetExecutionDetails(s_emptyContext);
-
- // Assert — static details should be reused
- Assert.Same(details1, details2);
- }
-
- [Fact]
- public void FactoryMethod_ReturnsHostedCodeInterpreterFileAgentSkillScriptExecutor()
- {
- // Act
- var executor = FileAgentSkillScriptExecutor.HostedCodeInterpreter();
-
- // Assert
- Assert.IsType(executor);
- }
-}