권한 모드 동등화: Plan 추가 및 claw-code 권한 별칭 정규화 반영
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- 전역 권한 모드를 Ask/Plan/Auto/Deny 4단계로 확장 - claw-code 계열 권한 값(default/acceptEdits/dontAsk/bypassPermissions) 입력 시 내부 모드로 정규화 - AgentContext 권한 판정 경로(전역/도구 오버라이드/패턴 오버라이드) 정규화 적용 - Chat/Settings UI에서 Plan 모드 노출 및 인라인 순환(Ask->Plan->Auto->Deny) 반영 - AppState/SettingsViewModel/SettingsService에 권한값 정규화 및 저장 시 일관성 적용 - Permission lifecycle 이벤트 메시지에 유효 모드 표기 보강 - 빌드/테스트 검증: dotnet build 경고0 오류0, dotnet test 372/372 통과
This commit is contained in:
@@ -591,7 +591,7 @@ public class LlmSettings
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 파일 접근 권한 수준.
|
/// 파일 접근 권한 수준.
|
||||||
/// Ask = 매번 확인 | Auto = 자동 허용 | Deny = 차단
|
/// Ask = 매번 확인 | Plan = 계획/승인 중심 | Auto = 자동 허용 | Deny = 차단
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonPropertyName("filePermission")]
|
[JsonPropertyName("filePermission")]
|
||||||
public string FilePermission { get; set; } = "Ask";
|
public string FilePermission { get; set; } = "Ask";
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ public class ChatConversation
|
|||||||
|
|
||||||
// ─── 대화별 설정 (하단 바에서 변경, 대화마다 독립 저장) ───
|
// ─── 대화별 설정 (하단 바에서 변경, 대화마다 독립 저장) ───
|
||||||
|
|
||||||
/// <summary>파일 접근 권한. null이면 전역 설정 사용. "Auto" | "Ask" | "Deny"</summary>
|
/// <summary>파일 접근 권한. null이면 전역 설정 사용. "Ask" | "Plan" | "Auto" | "Deny"</summary>
|
||||||
[JsonPropertyName("permission")]
|
[JsonPropertyName("permission")]
|
||||||
public string? Permission { get; set; }
|
public string? Permission { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -4068,6 +4068,9 @@ public partial class AgentLoopService
|
|||||||
case "ask":
|
case "ask":
|
||||||
normalized = "ask";
|
normalized = "ask";
|
||||||
return true;
|
return true;
|
||||||
|
case "plan":
|
||||||
|
normalized = "ask";
|
||||||
|
return true;
|
||||||
case "auto":
|
case "auto":
|
||||||
normalized = "auto";
|
normalized = "auto";
|
||||||
return true;
|
return true;
|
||||||
@@ -4153,16 +4156,16 @@ public partial class AgentLoopService
|
|||||||
messages,
|
messages,
|
||||||
success: true);
|
success: true);
|
||||||
|
|
||||||
var effectivePerm = context.GetEffectiveToolPermission(toolName, target);
|
var effectivePerm = PermissionModeCatalog.NormalizeGlobalMode(context.GetEffectiveToolPermission(toolName, target));
|
||||||
|
|
||||||
if (string.Equals(effectivePerm, "Ask", StringComparison.OrdinalIgnoreCase))
|
if (PermissionModeCatalog.RequiresUserApproval(effectivePerm))
|
||||||
EmitEvent(AgentEventType.PermissionRequest, toolName, $"권한 확인 필요 · 대상: {target}");
|
EmitEvent(AgentEventType.PermissionRequest, toolName, $"권한 확인 필요({effectivePerm}) · 대상: {target}");
|
||||||
|
|
||||||
var allowed = await context.CheckToolPermissionAsync(toolName, target);
|
var allowed = await context.CheckToolPermissionAsync(toolName, target);
|
||||||
if (allowed)
|
if (allowed)
|
||||||
{
|
{
|
||||||
if (string.Equals(effectivePerm, "Ask", StringComparison.OrdinalIgnoreCase))
|
if (PermissionModeCatalog.RequiresUserApproval(effectivePerm))
|
||||||
EmitEvent(AgentEventType.PermissionGranted, toolName, $"권한 승인됨 · 대상: {target}");
|
EmitEvent(AgentEventType.PermissionGranted, toolName, $"권한 승인됨({effectivePerm}) · 대상: {target}");
|
||||||
await RunPermissionLifecycleHooksAsync(
|
await RunPermissionLifecycleHooksAsync(
|
||||||
"__permission_granted__",
|
"__permission_granted__",
|
||||||
"post",
|
"post",
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class AgentContext
|
|||||||
/// <summary>작업 폴더 경로.</summary>
|
/// <summary>작업 폴더 경로.</summary>
|
||||||
public string WorkFolder { get; set; } = "";
|
public string WorkFolder { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>파일 접근 권한. Ask | Auto | Deny</summary>
|
/// <summary>파일 접근 권한. Ask | Plan | Auto | Deny</summary>
|
||||||
public string Permission { get; init; } = "Ask";
|
public string Permission { get; init; } = "Ask";
|
||||||
|
|
||||||
/// <summary>도구별 권한 오버라이드. 키: 도구명, 값: "ask" | "auto" | "deny".</summary>
|
/// <summary>도구별 권한 오버라이드. 키: 도구명, 값: "ask" | "auto" | "deny".</summary>
|
||||||
@@ -192,19 +192,21 @@ public class AgentContext
|
|||||||
public string GetEffectiveToolPermission(string toolName, string? target)
|
public string GetEffectiveToolPermission(string toolName, string? target)
|
||||||
{
|
{
|
||||||
if (TryResolvePatternPermission(toolName, target, out var patternPermission))
|
if (TryResolvePatternPermission(toolName, target, out var patternPermission))
|
||||||
return patternPermission;
|
return PermissionModeCatalog.NormalizeToolOverride(patternPermission);
|
||||||
|
|
||||||
if (ToolPermissions.TryGetValue(toolName, out var toolPerm) &&
|
if (ToolPermissions.TryGetValue(toolName, out var toolPerm) &&
|
||||||
!string.IsNullOrWhiteSpace(toolPerm))
|
!string.IsNullOrWhiteSpace(toolPerm))
|
||||||
return toolPerm;
|
return PermissionModeCatalog.NormalizeToolOverride(toolPerm);
|
||||||
if (ToolPermissions.TryGetValue("*", out var wildcardPerm) &&
|
if (ToolPermissions.TryGetValue("*", out var wildcardPerm) &&
|
||||||
!string.IsNullOrWhiteSpace(wildcardPerm))
|
!string.IsNullOrWhiteSpace(wildcardPerm))
|
||||||
return wildcardPerm;
|
return PermissionModeCatalog.NormalizeToolOverride(wildcardPerm);
|
||||||
if (ToolPermissions.TryGetValue("default", out var defaultPerm) &&
|
if (ToolPermissions.TryGetValue("default", out var defaultPerm) &&
|
||||||
!string.IsNullOrWhiteSpace(defaultPerm))
|
!string.IsNullOrWhiteSpace(defaultPerm))
|
||||||
return defaultPerm;
|
return PermissionModeCatalog.NormalizeToolOverride(defaultPerm);
|
||||||
|
|
||||||
return SensitiveTools.Contains(toolName) ? Permission : "Auto";
|
return SensitiveTools.Contains(toolName)
|
||||||
|
? PermissionModeCatalog.NormalizeGlobalMode(Permission)
|
||||||
|
: PermissionModeCatalog.Auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> CheckToolPermissionAsync(string toolName, string target)
|
public async Task<bool> CheckToolPermissionAsync(string toolName, string target)
|
||||||
@@ -213,9 +215,9 @@ public class AgentContext
|
|||||||
&& AxCopilot.Services.OperationModePolicy.IsBlockedAgentToolInInternalMode(toolName, target))
|
&& AxCopilot.Services.OperationModePolicy.IsBlockedAgentToolInInternalMode(toolName, target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var effectivePerm = GetEffectiveToolPermission(toolName, target);
|
var effectivePerm = PermissionModeCatalog.NormalizeGlobalMode(GetEffectiveToolPermission(toolName, target));
|
||||||
if (string.Equals(effectivePerm, "Deny", StringComparison.OrdinalIgnoreCase)) return false;
|
if (PermissionModeCatalog.IsDeny(effectivePerm)) return false;
|
||||||
if (string.Equals(effectivePerm, "Auto", StringComparison.OrdinalIgnoreCase)) return true;
|
if (PermissionModeCatalog.IsAuto(effectivePerm)) return true;
|
||||||
if (AskPermission == null) return false;
|
if (AskPermission == null) return false;
|
||||||
|
|
||||||
var normalizedTarget = string.IsNullOrWhiteSpace(target) ? toolName : target.Trim();
|
var normalizedTarget = string.IsNullOrWhiteSpace(target) ? toolName : target.Trim();
|
||||||
|
|||||||
72
src/AxCopilot/Services/Agent/PermissionModeCatalog.cs
Normal file
72
src/AxCopilot/Services/Agent/PermissionModeCatalog.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
namespace AxCopilot.Services.Agent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AX Agent permission mode constants and normalization helpers.
|
||||||
|
/// Accepts legacy Ask/Auto/Deny values plus claw-code style aliases.
|
||||||
|
/// </summary>
|
||||||
|
public static class PermissionModeCatalog
|
||||||
|
{
|
||||||
|
public const string Ask = "Ask";
|
||||||
|
public const string Plan = "Plan";
|
||||||
|
public const string Auto = "Auto";
|
||||||
|
public const string Deny = "Deny";
|
||||||
|
|
||||||
|
public static readonly IReadOnlyList<string> UserSelectableModes = new[]
|
||||||
|
{
|
||||||
|
Ask,
|
||||||
|
Plan,
|
||||||
|
Auto,
|
||||||
|
Deny,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalize global permission mode.
|
||||||
|
/// Supported aliases: ask/auto/deny/plan plus default, acceptEdits, dontAsk, bypassPermissions.
|
||||||
|
/// </summary>
|
||||||
|
public static string NormalizeGlobalMode(string? value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
return Ask;
|
||||||
|
|
||||||
|
return value.Trim().ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"ask" => Ask,
|
||||||
|
"default" => Ask,
|
||||||
|
"plan" => Plan,
|
||||||
|
"auto" => Auto,
|
||||||
|
"acceptedits" => Auto,
|
||||||
|
"dontask" => Auto,
|
||||||
|
"deny" => Deny,
|
||||||
|
"bypasspermissions" => Deny,
|
||||||
|
_ => Ask,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalize tool override permission.
|
||||||
|
/// Tool override is constrained to ask/auto/deny.
|
||||||
|
/// </summary>
|
||||||
|
public static string NormalizeToolOverride(string? value)
|
||||||
|
{
|
||||||
|
var mode = NormalizeGlobalMode(value);
|
||||||
|
return mode switch
|
||||||
|
{
|
||||||
|
Auto => "auto",
|
||||||
|
Deny => "deny",
|
||||||
|
_ => "ask",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsAuto(string? mode) =>
|
||||||
|
string.Equals(NormalizeGlobalMode(mode), Auto, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public static bool IsDeny(string? mode) =>
|
||||||
|
string.Equals(NormalizeGlobalMode(mode), Deny, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public static bool RequiresUserApproval(string? mode)
|
||||||
|
{
|
||||||
|
var normalized = NormalizeGlobalMode(mode);
|
||||||
|
return !string.Equals(normalized, Auto, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& !string.Equals(normalized, Deny, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -194,7 +194,7 @@ public sealed class AppStateService
|
|||||||
Skills.Enabled = llm.EnableSkillSystem;
|
Skills.Enabled = llm.EnableSkillSystem;
|
||||||
Skills.FolderPath = llm.SkillsFolderPath ?? "";
|
Skills.FolderPath = llm.SkillsFolderPath ?? "";
|
||||||
|
|
||||||
Permissions.FilePermission = llm.FilePermission ?? "Ask";
|
Permissions.FilePermission = PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission);
|
||||||
Permissions.AgentDecisionLevel = llm.AgentDecisionLevel ?? "detailed";
|
Permissions.AgentDecisionLevel = llm.AgentDecisionLevel ?? "detailed";
|
||||||
Permissions.PlanMode = llm.PlanMode ?? "off";
|
Permissions.PlanMode = llm.PlanMode ?? "off";
|
||||||
Permissions.ToolOverrideCount = llm.ToolPermissions?.Count ?? 0;
|
Permissions.ToolOverrideCount = llm.ToolPermissions?.Count ?? 0;
|
||||||
@@ -501,27 +501,31 @@ public sealed class AppStateService
|
|||||||
|
|
||||||
public PermissionSummaryState GetPermissionSummary(ChatConversation? conversation = null)
|
public PermissionSummaryState GetPermissionSummary(ChatConversation? conversation = null)
|
||||||
{
|
{
|
||||||
var effective = conversation?.Permission;
|
var effective = PermissionModeCatalog.NormalizeGlobalMode(conversation?.Permission);
|
||||||
if (string.IsNullOrWhiteSpace(effective))
|
var defaultMode = PermissionModeCatalog.NormalizeGlobalMode(Permissions.FilePermission);
|
||||||
effective = Permissions.FilePermission;
|
if (string.IsNullOrWhiteSpace(conversation?.Permission))
|
||||||
|
effective = defaultMode;
|
||||||
|
|
||||||
var risk = string.Equals(effective, "Auto", StringComparison.OrdinalIgnoreCase)
|
var risk = string.Equals(effective, PermissionModeCatalog.Auto, StringComparison.OrdinalIgnoreCase)
|
||||||
? "high"
|
? "high"
|
||||||
: string.Equals(effective, "Deny", StringComparison.OrdinalIgnoreCase)
|
: string.Equals(effective, PermissionModeCatalog.Deny, StringComparison.OrdinalIgnoreCase)
|
||||||
? "locked"
|
? "locked"
|
||||||
|
: string.Equals(effective, PermissionModeCatalog.Plan, StringComparison.OrdinalIgnoreCase)
|
||||||
|
? "guarded"
|
||||||
: "normal";
|
: "normal";
|
||||||
|
|
||||||
var description = effective switch
|
var description = effective switch
|
||||||
{
|
{
|
||||||
"Auto" => "파일 작업을 자동 허용합니다.",
|
"Auto" => "파일 작업을 자동 허용합니다.",
|
||||||
"Deny" => "파일 작업을 차단합니다.",
|
"Deny" => "파일 작업을 차단합니다.",
|
||||||
|
"Plan" => "계획/승인 흐름을 우선 적용한 뒤 파일 작업을 진행합니다.",
|
||||||
_ => "파일 작업 전마다 사용자 확인을 요청합니다.",
|
_ => "파일 작업 전마다 사용자 확인을 요청합니다.",
|
||||||
};
|
};
|
||||||
|
|
||||||
return new PermissionSummaryState
|
return new PermissionSummaryState
|
||||||
{
|
{
|
||||||
EffectiveMode = effective ?? "Ask",
|
EffectiveMode = effective,
|
||||||
DefaultMode = Permissions.FilePermission,
|
DefaultMode = defaultMode,
|
||||||
OverrideCount = Permissions.ToolOverrideCount,
|
OverrideCount = Permissions.ToolOverrideCount,
|
||||||
RiskLevel = risk,
|
RiskLevel = risk,
|
||||||
Description = description,
|
Description = description,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using AxCopilot.Models;
|
using AxCopilot.Models;
|
||||||
|
using AxCopilot.Services.Agent;
|
||||||
|
|
||||||
namespace AxCopilot.Services;
|
namespace AxCopilot.Services;
|
||||||
|
|
||||||
@@ -173,6 +174,15 @@ public class SettingsService
|
|||||||
|
|
||||||
private void NormalizeRuntimeSettings()
|
private void NormalizeRuntimeSettings()
|
||||||
{
|
{
|
||||||
|
_settings.Llm.FilePermission = PermissionModeCatalog.NormalizeGlobalMode(_settings.Llm.FilePermission);
|
||||||
|
_settings.Llm.DefaultAgentPermission = PermissionModeCatalog.NormalizeGlobalMode(_settings.Llm.DefaultAgentPermission);
|
||||||
|
if (_settings.Llm.ToolPermissions != null && _settings.Llm.ToolPermissions.Count > 0)
|
||||||
|
{
|
||||||
|
var keys = _settings.Llm.ToolPermissions.Keys.ToList();
|
||||||
|
foreach (var key in keys)
|
||||||
|
_settings.Llm.ToolPermissions[key] = PermissionModeCatalog.NormalizeToolOverride(_settings.Llm.ToolPermissions[key]);
|
||||||
|
}
|
||||||
|
|
||||||
NormalizeLlmThresholds(_settings.Llm);
|
NormalizeLlmThresholds(_settings.Llm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,3 +272,4 @@ public class SettingsService
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Windows.Forms;
|
|||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using AxCopilot.Models;
|
using AxCopilot.Models;
|
||||||
using AxCopilot.Services;
|
using AxCopilot.Services;
|
||||||
|
using AxCopilot.Services.Agent;
|
||||||
using AxCopilot.Themes;
|
using AxCopilot.Themes;
|
||||||
using AxCopilot.Views;
|
using AxCopilot.Views;
|
||||||
|
|
||||||
@@ -252,7 +253,7 @@ public class SettingsViewModel : INotifyPropertyChanged
|
|||||||
public string DefaultAgentPermission
|
public string DefaultAgentPermission
|
||||||
{
|
{
|
||||||
get => _defaultAgentPermission;
|
get => _defaultAgentPermission;
|
||||||
set { _defaultAgentPermission = value; OnPropertyChanged(); }
|
set { _defaultAgentPermission = PermissionModeCatalog.NormalizeGlobalMode(value); OnPropertyChanged(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 코워크/에이전트 고급 설정 ──
|
// ── 코워크/에이전트 고급 설정 ──
|
||||||
@@ -981,7 +982,7 @@ public class SettingsViewModel : INotifyPropertyChanged
|
|||||||
_llmMaxContextTokens = llm.MaxContextTokens;
|
_llmMaxContextTokens = llm.MaxContextTokens;
|
||||||
_llmRetentionDays = llm.RetentionDays;
|
_llmRetentionDays = llm.RetentionDays;
|
||||||
_llmTemperature = llm.Temperature;
|
_llmTemperature = llm.Temperature;
|
||||||
_defaultAgentPermission = llm.DefaultAgentPermission;
|
_defaultAgentPermission = PermissionModeCatalog.NormalizeGlobalMode(llm.DefaultAgentPermission);
|
||||||
_defaultOutputFormat = llm.DefaultOutputFormat;
|
_defaultOutputFormat = llm.DefaultOutputFormat;
|
||||||
_defaultMood = string.IsNullOrEmpty(llm.DefaultMood) ? "modern" : llm.DefaultMood;
|
_defaultMood = string.IsNullOrEmpty(llm.DefaultMood) ? "modern" : llm.DefaultMood;
|
||||||
_autoPreview = llm.AutoPreview;
|
_autoPreview = llm.AutoPreview;
|
||||||
@@ -1405,7 +1406,7 @@ public class SettingsViewModel : INotifyPropertyChanged
|
|||||||
s.Llm.MaxContextTokens = _llmMaxContextTokens;
|
s.Llm.MaxContextTokens = _llmMaxContextTokens;
|
||||||
s.Llm.RetentionDays = _llmRetentionDays;
|
s.Llm.RetentionDays = _llmRetentionDays;
|
||||||
s.Llm.Temperature = _llmTemperature;
|
s.Llm.Temperature = _llmTemperature;
|
||||||
s.Llm.DefaultAgentPermission = _defaultAgentPermission;
|
s.Llm.DefaultAgentPermission = PermissionModeCatalog.NormalizeGlobalMode(_defaultAgentPermission);
|
||||||
s.Llm.DefaultOutputFormat = _defaultOutputFormat;
|
s.Llm.DefaultOutputFormat = _defaultOutputFormat;
|
||||||
s.Llm.DefaultMood = _defaultMood;
|
s.Llm.DefaultMood = _defaultMood;
|
||||||
s.Llm.AutoPreview = _autoPreview;
|
s.Llm.AutoPreview = _autoPreview;
|
||||||
|
|||||||
@@ -1282,7 +1282,7 @@ public partial class ChatWindow : Window
|
|||||||
var llm = _settings.Settings.Llm;
|
var llm = _settings.Settings.Llm;
|
||||||
|
|
||||||
if (conv != null && conv.Permission != null)
|
if (conv != null && conv.Permission != null)
|
||||||
_settings.Settings.Llm.FilePermission = conv.Permission;
|
_settings.Settings.Llm.FilePermission = PermissionModeCatalog.NormalizeGlobalMode(conv.Permission);
|
||||||
|
|
||||||
_folderDataUsage = conv?.DataUsage ?? llm.FolderDataUsage ?? "active";
|
_folderDataUsage = conv?.DataUsage ?? llm.FolderDataUsage ?? "active";
|
||||||
_selectedMood = conv?.Mood ?? llm.DefaultMood ?? "modern";
|
_selectedMood = conv?.Mood ?? llm.DefaultMood ?? "modern";
|
||||||
@@ -1304,7 +1304,7 @@ public partial class ChatWindow : Window
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
conv.Permission = _settings.Settings.Llm.FilePermission;
|
conv.Permission = PermissionModeCatalog.NormalizeGlobalMode(_settings.Settings.Llm.FilePermission);
|
||||||
conv.DataUsage = _folderDataUsage;
|
conv.DataUsage = _folderDataUsage;
|
||||||
conv.Mood = _selectedMood;
|
conv.Mood = _selectedMood;
|
||||||
_storage.Save(conv);
|
_storage.Save(conv);
|
||||||
@@ -1485,10 +1485,11 @@ public partial class ChatWindow : Window
|
|||||||
|
|
||||||
var levels = new (string Level, string Sym, string Desc, string Color)[] {
|
var levels = new (string Level, string Sym, string Desc, string Color)[] {
|
||||||
("Ask", "\uE8D7", "매번 확인 — 파일 접근 시 사용자에게 묻습니다", "#4B5EFC"),
|
("Ask", "\uE8D7", "매번 확인 — 파일 접근 시 사용자에게 묻습니다", "#4B5EFC"),
|
||||||
|
("Plan", "\uE7C3", "계획/승인 중심 — 실행 전 계획과 사용자 확인 흐름을 우선합니다", "#4338CA"),
|
||||||
("Auto", "\uE73E", "자동 허용 — 파일을 자동으로 읽고 씁니다", "#DD6B20"),
|
("Auto", "\uE73E", "자동 허용 — 파일을 자동으로 읽고 씁니다", "#DD6B20"),
|
||||||
("Deny", "\uE711", "접근 차단 — 파일 접근을 허용하지 않습니다", "#C50F1F"),
|
("Deny", "\uE711", "접근 차단 — 파일 접근을 허용하지 않습니다", "#C50F1F"),
|
||||||
};
|
};
|
||||||
var current = _settings.Settings.Llm.FilePermission;
|
var current = PermissionModeCatalog.NormalizeGlobalMode(_settings.Settings.Llm.FilePermission);
|
||||||
foreach (var (level, sym, desc, color) in levels)
|
foreach (var (level, sym, desc, color) in levels)
|
||||||
{
|
{
|
||||||
var isActive = level.Equals(current, StringComparison.OrdinalIgnoreCase);
|
var isActive = level.Equals(current, StringComparison.OrdinalIgnoreCase);
|
||||||
@@ -1548,7 +1549,7 @@ public partial class ChatWindow : Window
|
|||||||
var capturedLevel = level;
|
var capturedLevel = level;
|
||||||
btn.Click += (_, _) =>
|
btn.Click += (_, _) =>
|
||||||
{
|
{
|
||||||
_settings.Settings.Llm.FilePermission = capturedLevel;
|
_settings.Settings.Llm.FilePermission = PermissionModeCatalog.NormalizeGlobalMode(capturedLevel);
|
||||||
UpdatePermissionUI();
|
UpdatePermissionUI();
|
||||||
SaveConversationSettings();
|
SaveConversationSettings();
|
||||||
PermissionPopup.IsOpen = false;
|
PermissionPopup.IsOpen = false;
|
||||||
@@ -1571,7 +1572,7 @@ public partial class ChatWindow : Window
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
toolPermissions[existingKey ?? toolName] = mode;
|
toolPermissions[existingKey ?? toolName] = PermissionModeCatalog.NormalizeToolOverride(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
try { _settings.Save(); } catch { }
|
try { _settings.Save(); } catch { }
|
||||||
@@ -1601,10 +1602,11 @@ public partial class ChatWindow : Window
|
|||||||
ChatConversation? currentConversation;
|
ChatConversation? currentConversation;
|
||||||
lock (_convLock) currentConversation = _currentConversation;
|
lock (_convLock) currentConversation = _currentConversation;
|
||||||
var summary = _appState.GetPermissionSummary(currentConversation);
|
var summary = _appState.GetPermissionSummary(currentConversation);
|
||||||
var perm = summary.EffectiveMode;
|
var perm = PermissionModeCatalog.NormalizeGlobalMode(summary.EffectiveMode);
|
||||||
PermissionLabel.Text = perm;
|
PermissionLabel.Text = perm;
|
||||||
PermissionIcon.Text = perm switch
|
PermissionIcon.Text = perm switch
|
||||||
{
|
{
|
||||||
|
"Plan" => "\uE7C3",
|
||||||
"Auto" => "\uE73E",
|
"Auto" => "\uE73E",
|
||||||
"Deny" => "\uE711",
|
"Deny" => "\uE711",
|
||||||
_ => "\uE8D7",
|
_ => "\uE8D7",
|
||||||
@@ -1629,8 +1631,12 @@ public partial class ChatWindow : Window
|
|||||||
{
|
{
|
||||||
_autoWarningDismissed = false; // Auto가 아닌 모드로 전환하면 리셋
|
_autoWarningDismissed = false; // Auto가 아닌 모드로 전환하면 리셋
|
||||||
var defaultFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
var defaultFg = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||||
var iconFg = perm == "Deny" ? new SolidColorBrush(Color.FromRgb(0xC5, 0x0F, 0x1F))
|
var iconFg = perm switch
|
||||||
: new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC)); // Ask = 파란색
|
{
|
||||||
|
"Deny" => new SolidColorBrush(Color.FromRgb(0xC5, 0x0F, 0x1F)),
|
||||||
|
"Plan" => new SolidColorBrush(Color.FromRgb(0x43, 0x38, 0xCA)),
|
||||||
|
_ => new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC)), // Ask = 파란색
|
||||||
|
};
|
||||||
PermissionLabel.Foreground = defaultFg;
|
PermissionLabel.Foreground = defaultFg;
|
||||||
PermissionIcon.Foreground = iconFg;
|
PermissionIcon.Foreground = iconFg;
|
||||||
if (AutoPermissionWarning != null)
|
if (AutoPermissionWarning != null)
|
||||||
@@ -1739,11 +1745,11 @@ public partial class ChatWindow : Window
|
|||||||
if (_activeTab == "Chat")
|
if (_activeTab == "Chat")
|
||||||
{
|
{
|
||||||
// Chat 탭: 경고 배너 숨기고 기본 Ask 모드로 복원
|
// Chat 탭: 경고 배너 숨기고 기본 Ask 모드로 복원
|
||||||
_settings.Settings.Llm.FilePermission = "Ask";
|
_settings.Settings.Llm.FilePermission = PermissionModeCatalog.Ask;
|
||||||
UpdatePermissionUI();
|
UpdatePermissionUI();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var defaultPerm = _settings.Settings.Llm.DefaultAgentPermission;
|
var defaultPerm = PermissionModeCatalog.NormalizeGlobalMode(_settings.Settings.Llm.DefaultAgentPermission);
|
||||||
if (!string.IsNullOrEmpty(defaultPerm))
|
if (!string.IsNullOrEmpty(defaultPerm))
|
||||||
{
|
{
|
||||||
_settings.Settings.Llm.FilePermission = defaultPerm;
|
_settings.Settings.Llm.FilePermission = defaultPerm;
|
||||||
@@ -10318,9 +10324,10 @@ public partial class ChatWindow : Window
|
|||||||
"detailed" => "높음",
|
"detailed" => "높음",
|
||||||
_ => "중간",
|
_ => "중간",
|
||||||
};
|
};
|
||||||
private static string NextPermission(string current) => (current ?? "Ask").ToLowerInvariant() switch
|
private static string NextPermission(string current) => PermissionModeCatalog.NormalizeGlobalMode(current).ToLowerInvariant() switch
|
||||||
{
|
{
|
||||||
"ask" => "Auto",
|
"ask" => "Plan",
|
||||||
|
"plan" => "Auto",
|
||||||
"auto" => "Deny",
|
"auto" => "Deny",
|
||||||
_ => "Ask",
|
_ => "Ask",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4346,6 +4346,7 @@
|
|||||||
Width="160" SelectedValue="{Binding DefaultAgentPermission, Mode=TwoWay}"
|
Width="160" SelectedValue="{Binding DefaultAgentPermission, Mode=TwoWay}"
|
||||||
SelectedValuePath="Tag">
|
SelectedValuePath="Tag">
|
||||||
<ComboBoxItem Content="Ask (매번 확인)" Tag="Ask"/>
|
<ComboBoxItem Content="Ask (매번 확인)" Tag="Ask"/>
|
||||||
|
<ComboBoxItem Content="Plan (계획/승인 중심)" Tag="Plan"/>
|
||||||
<ComboBoxItem Content="Auto (자동 허용)" Tag="Auto"/>
|
<ComboBoxItem Content="Auto (자동 허용)" Tag="Auto"/>
|
||||||
<ComboBoxItem Content="Deny (차단)" Tag="Deny"/>
|
<ComboBoxItem Content="Deny (차단)" Tag="Deny"/>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|||||||
Reference in New Issue
Block a user