claw-code 동등 품질 4단계 연속 반영: Agentic 루프/상태복원/설정연동/릴리즈 게이트 정렬
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- 도구 동등화: task/todo/tool-search + plan/worktree/team/cron 도구군 추가 및 ToolRegistry 등록\n- claw-code CamelCase 별칭 정규화 확장: EnterPlanMode/EnterWorktree/TeamCreate/CronCreate 등 -> 내부 snake_case 매핑\n- AgentLoop 런타임 강화: Code 탭 전용 도구 토글(CodeSettings) 반영, 비활성 도구 자동 차단\n- Worktree 상태 복원 연결: .ax/worktree_state.json 기반 루트 탐색/활성 worktree 복원 및 BuildContext 연동\n- 권한/플러그인 하드닝 기존 반영분 유지: target 기반 권한 판정 + internal 모드 플러그인 경로/manifest 검증\n- 설정 연동(UI): SettingsWindow Code 패널에 Plan/Worktree/Team/Cron 도구 on/off 토글 추가\n- 테스트 보강: AgentParityTools/AgentLoopE2E에 worktree 지속성, alias 정규화, 설정 차단 시나리오 추가\n- 검증 완료: dotnet build(경고0/오류0), ParityBenchmark 11/11, ReplayStability 12/12, 전체 371/371, release-gate 통과\n- 문서 동기화: AGENT_ROADMAP/NEXT_ROADMAP/CLAW_CODE_PARITY_PLAN 수치 및 기준 최신화
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace AxCopilot.Services.Agent;
|
||||
@@ -95,7 +96,7 @@ public class AgentContext
|
||||
private readonly object _permissionLock = new();
|
||||
private readonly HashSet<string> _approvedPermissionCache = new(StringComparer.OrdinalIgnoreCase);
|
||||
/// <summary>작업 폴더 경로.</summary>
|
||||
public string WorkFolder { get; init; } = "";
|
||||
public string WorkFolder { get; set; } = "";
|
||||
|
||||
/// <summary>파일 접근 권한. Ask | Auto | Deny</summary>
|
||||
public string Permission { get; init; } = "Ask";
|
||||
@@ -186,8 +187,13 @@ public class AgentContext
|
||||
return await CheckToolPermissionAsync(toolName, filePath);
|
||||
}
|
||||
|
||||
public string GetEffectiveToolPermission(string toolName)
|
||||
public string GetEffectiveToolPermission(string toolName) => GetEffectiveToolPermission(toolName, null);
|
||||
|
||||
public string GetEffectiveToolPermission(string toolName, string? target)
|
||||
{
|
||||
if (TryResolvePatternPermission(toolName, target, out var patternPermission))
|
||||
return patternPermission;
|
||||
|
||||
if (ToolPermissions.TryGetValue(toolName, out var toolPerm) &&
|
||||
!string.IsNullOrWhiteSpace(toolPerm))
|
||||
return toolPerm;
|
||||
@@ -207,7 +213,7 @@ public class AgentContext
|
||||
&& AxCopilot.Services.OperationModePolicy.IsBlockedAgentToolInInternalMode(toolName, target))
|
||||
return false;
|
||||
|
||||
var effectivePerm = GetEffectiveToolPermission(toolName);
|
||||
var effectivePerm = GetEffectiveToolPermission(toolName, target);
|
||||
if (string.Equals(effectivePerm, "Deny", StringComparison.OrdinalIgnoreCase)) return false;
|
||||
if (string.Equals(effectivePerm, "Auto", StringComparison.OrdinalIgnoreCase)) return true;
|
||||
if (AskPermission == null) return false;
|
||||
@@ -228,6 +234,67 @@ public class AgentContext
|
||||
}
|
||||
return allowed;
|
||||
}
|
||||
|
||||
private bool TryResolvePatternPermission(string toolName, string? target, out string permission)
|
||||
{
|
||||
permission = "";
|
||||
if (ToolPermissions.Count == 0 || string.IsNullOrWhiteSpace(target))
|
||||
return false;
|
||||
|
||||
var normalizedTool = toolName.Trim();
|
||||
var normalizedTarget = target.Trim();
|
||||
|
||||
foreach (var kv in ToolPermissions)
|
||||
{
|
||||
if (TryParsePatternRule(kv.Key, out var ruleTool, out var rulePattern)
|
||||
&& string.Equals(ruleTool, normalizedTool, StringComparison.OrdinalIgnoreCase)
|
||||
&& WildcardMatch(normalizedTarget, rulePattern)
|
||||
&& !string.IsNullOrWhiteSpace(kv.Value))
|
||||
{
|
||||
permission = kv.Value.Trim();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kv in ToolPermissions)
|
||||
{
|
||||
if (TryParsePatternRule(kv.Key, out var ruleTool, out var rulePattern)
|
||||
&& string.Equals(ruleTool, "*", StringComparison.Ordinal)
|
||||
&& WildcardMatch(normalizedTarget, rulePattern)
|
||||
&& !string.IsNullOrWhiteSpace(kv.Value))
|
||||
{
|
||||
permission = kv.Value.Trim();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryParsePatternRule(string? key, out string ruleTool, out string rulePattern)
|
||||
{
|
||||
ruleTool = "";
|
||||
rulePattern = "";
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
return false;
|
||||
|
||||
var trimmed = key.Trim();
|
||||
var at = trimmed.IndexOf('@');
|
||||
if (at <= 0 || at == trimmed.Length - 1)
|
||||
return false;
|
||||
|
||||
ruleTool = trimmed[..at].Trim();
|
||||
rulePattern = trimmed[(at + 1)..].Trim();
|
||||
return !string.IsNullOrWhiteSpace(ruleTool) && !string.IsNullOrWhiteSpace(rulePattern);
|
||||
}
|
||||
|
||||
private static bool WildcardMatch(string input, string pattern)
|
||||
{
|
||||
var regex = "^" + Regex.Escape(pattern)
|
||||
.Replace("\\*", ".*")
|
||||
.Replace("\\?", ".") + "$";
|
||||
return Regex.IsMatch(input, regex, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>에이전트 이벤트 (UI 표시용).</summary>
|
||||
|
||||
Reference in New Issue
Block a user