스킬 런타임 2차 고도화와 도구 노출 필터 정비
프로젝트 .claude/skills 재귀 로드와 namespaced SKILL.md 파싱을 추가하고 번들/사용자/프로젝트 스킬을 함께 노출하도록 SkillService와 설정 UI를 확장했다. 슬래시 스킬 호출 시 인자 치환, 스킬 폴더 변수 치환, inline shell 실행, when_to_use 기반 자동 스킬 가이드를 실제 ChatWindow 런타임 경로에 연결했다. blanket deny 권한은 모델 노출 전 활성 도구 목록에서 먼저 제외하도록 AgentLoopService를 보강했고 관련 테스트와 README/DEVELOPMENT 문서를 업데이트했다. 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_phase2\\ -p:IntermediateOutputPath=obj\\verify_phase2\\ (경고 0 / 오류 0) 검증: dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AgentToolCatalogTests|SkillServiceRuntimePolicyTests" -p:OutputPath=bin\\verify_phase2_tests\\ -p:IntermediateOutputPath=obj\\verify_phase2_tests\\ (통과 16, 기존 WorkspaceContextGeneratorTests nullable 경고 1건 유지)
This commit is contained in:
@@ -2010,6 +2010,7 @@ public partial class AgentLoopService
|
||||
var mergedDisabled = MergeDisabledTools(disabledToolNames);
|
||||
// 탭별 도구 필터링: 현재 탭에 맞는 도구만 LLM에 전송 (토큰 절약)
|
||||
var active = _tools.GetActiveToolsForTab(ActiveTab, mergedDisabled);
|
||||
active = ApplyPermissionExposureFilter(active);
|
||||
if (runtimeOverrides == null || runtimeOverrides.AllowedToolNames.Count == 0)
|
||||
return active;
|
||||
|
||||
@@ -2019,6 +2020,51 @@ public partial class AgentLoopService
|
||||
.AsReadOnly();
|
||||
}
|
||||
|
||||
private IReadOnlyCollection<IAgentTool> ApplyPermissionExposureFilter(IReadOnlyCollection<IAgentTool> tools)
|
||||
{
|
||||
var blanketDeniedTools = GetBlanketDeniedToolNames();
|
||||
if (blanketDeniedTools.Count == 0)
|
||||
return tools;
|
||||
|
||||
if (blanketDeniedTools.Contains("*"))
|
||||
return Array.Empty<IAgentTool>();
|
||||
|
||||
return tools
|
||||
.Where(tool => !blanketDeniedTools.Contains(tool.Name))
|
||||
.ToList()
|
||||
.AsReadOnly();
|
||||
}
|
||||
|
||||
private HashSet<string> GetBlanketDeniedToolNames()
|
||||
{
|
||||
var result = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var permissionMap = AgentToolCatalog.CanonicalizePermissionMap(_settings.Settings.Llm.ToolPermissions ?? new Dictionary<string, string>());
|
||||
foreach (var kv in permissionMap)
|
||||
{
|
||||
if (!PermissionModeCatalog.IsDeny(kv.Value))
|
||||
continue;
|
||||
|
||||
var key = kv.Key?.Trim() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
continue;
|
||||
|
||||
if (key.IndexOfAny(['@', '|', '(', ')']) >= 0)
|
||||
continue;
|
||||
|
||||
if (string.Equals(key, "*", StringComparison.Ordinal))
|
||||
{
|
||||
result.Add("*");
|
||||
return result;
|
||||
}
|
||||
|
||||
var canonical = AgentToolCatalog.Canonicalize(key);
|
||||
if (!string.IsNullOrWhiteSpace(canonical))
|
||||
result.Add(canonical);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<string> MergeDisabledTools(IEnumerable<string>? disabledToolNames)
|
||||
{
|
||||
var disabled = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
Reference in New Issue
Block a user