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
1 change: 0 additions & 1 deletion dotnet/agent-framework-dotnet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@
<Folder Name="/Samples/02-agents/AgentSkills/">
<File Path="samples/02-agents/AgentSkills/README.md" />
<Project Path="samples/02-agents/AgentSkills/Agent_Step01_BasicSkills/Agent_Step01_BasicSkills.csproj" />
<Project Path="samples/02-agents/AgentSkills/Agent_Step02_ScriptExecutionWithCodeInterpreter/Agent_Step02_ScriptExecutionWithCodeInterpreter.csproj" />
</Folder>
<Folder Name="/Samples/02-agents/AGUI/Step05_StateManagement/">
<Project Path="samples/02-agents/AGUI/Step05_StateManagement/Client/Client.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

1 change: 0 additions & 1 deletion dotnet/samples/02-agents/AgentSkills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
21 changes: 9 additions & 12 deletions dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkill.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.
/// </remarks>
[Experimental(DiagnosticIds.Experiments.AgentsAIExperiments)]
public sealed class FileAgentSkill
internal sealed class FileAgentSkill
{
/// <summary>
/// Initializes a new instance of the <see cref="FileAgentSkill"/> class.
Expand All @@ -25,8 +22,8 @@ public sealed class FileAgentSkill
/// <param name="body">The SKILL.md content after the closing <c>---</c> delimiter.</param>
/// <param name="sourcePath">Absolute path to the directory containing this skill.</param>
/// <param name="resourceNames">Relative paths of resource files referenced in the skill body.</param>
internal FileAgentSkill(
FileAgentSkillFrontmatter frontmatter,
public FileAgentSkill(
SkillFrontmatter frontmatter,
string body,
string sourcePath,
IReadOnlyList<string>? resourceNames = null)
Expand All @@ -40,20 +37,20 @@ internal FileAgentSkill(
/// <summary>
/// Gets the parsed YAML frontmatter (name and description).
/// </summary>
public FileAgentSkillFrontmatter Frontmatter { get; }
public SkillFrontmatter Frontmatter { get; }

/// <summary>
/// Gets the directory path where the skill was discovered.
/// Gets the SKILL.md body content (without the YAML frontmatter).
/// </summary>
public string SourcePath { get; }
public string Body { get; }

/// <summary>
/// Gets the SKILL.md body content (without the YAML frontmatter).
/// Gets the directory path where the skill was discovered.
/// </summary>
internal string Body { get; }
public string SourcePath { get; }

/// <summary>
/// Gets the relative paths of resource files referenced in the skill body (e.g., "references/FAQ.md").
/// </summary>
internal IReadOnlyList<string> ResourceNames { get; }
public IReadOnlyList<string> ResourceNames { get; }
}
22 changes: 8 additions & 14 deletions dotnet/src/Microsoft.Agents.AI/Skills/FileAgentSkillLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.DiagnosticIds;

namespace Microsoft.Agents.AI;

Expand All @@ -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.
/// </remarks>
[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;
Expand All @@ -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.
Expand Down Expand Up @@ -117,7 +111,7 @@ internal Dictionary<string, FileAgentSkill> DiscoverAndLoadSkills(IEnumerable<st
/// <exception cref="InvalidOperationException">
/// The resource is not registered, resolves outside the skill directory, or does not exist.
/// </exception>
public async Task<string> ReadSkillResourceAsync(FileAgentSkill skill, string resourceName, CancellationToken cancellationToken = default)
internal async Task<string> ReadSkillResourceAsync(FileAgentSkill skill, string resourceName, CancellationToken cancellationToken = default)
{
resourceName = NormalizeResourcePath(resourceName);

Expand Down Expand Up @@ -195,7 +189,7 @@ private static void SearchDirectoriesForSkills(string directory, List<string> 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;
}
Expand All @@ -214,7 +208,7 @@ private static void SearchDirectoriesForSkills(string directory, List<string> 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!;
Expand Down Expand Up @@ -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;
Expand Down
Loading
Loading