AX Agent 코워크·코드 흐름과 컨텍스트 관리를 claude-code 기준으로 대폭 정리
- 코워크·코드 프롬프트, 도구 선택, 문서 생성/검증 흐름을 claude-code 동등 품질 기준으로 재정렬함 - OpenAI/vLLM 경로의 오래된 tool history를 평탄화하고 최근 이력만 구조화해 컨텍스트 직렬화를 경량화함 - AX Agent UI를 테마 기준으로 재구성하고 플랜 승인/오버레이/이벤트 렌더링/명령 입력 상호작용을 개선함 - 파일 후보 제안, 반복 경로 정체 복구, LSP 보강, 문서·PPT 처리 개선, 설정/서비스 인터페이스 정리를 함께 반영함 - README.md 및 docs/DEVELOPMENT.md를 작업 시점별로 갱신함 - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0, 오류 0)
This commit is contained in:
@@ -20,6 +20,14 @@ public interface IAgentTool
|
||||
/// <summary>LLM function calling용 파라미터 JSON Schema.</summary>
|
||||
ToolParameterSchema Parameters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 도구가 활성화되는 탭 카테고리.
|
||||
/// null이면 모든 탭에서 사용 가능.
|
||||
/// "Cowork" = Cowork 전용, "Code" = Code 전용, "Chat" = Chat 전용.
|
||||
/// 쉼표 구분으로 복수 탭 지정 가능: "Cowork,Code".
|
||||
/// </summary>
|
||||
string? TabCategory => null;
|
||||
|
||||
/// <summary>도구를 실행하고 결과를 반환합니다.</summary>
|
||||
/// <param name="args">LLM이 생성한 JSON 파라미터</param>
|
||||
/// <param name="context">실행 컨텍스트 (작업 폴더, 권한 등)</param>
|
||||
@@ -171,16 +179,35 @@ public class AgentContext
|
||||
}
|
||||
|
||||
// 작업 폴더 제한: 작업 폴더가 설정되어 있으면 하위 경로만 허용
|
||||
// 사내 모드에서는 외부 경로도 사용자 승인 후 접근 가능 (CheckToolPermissionAsync에서 강제 승인 처리)
|
||||
if (!string.IsNullOrEmpty(WorkFolder))
|
||||
{
|
||||
var workFull = Path.GetFullPath(WorkFolder);
|
||||
if (!fullPath.StartsWith(workFull, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// 사내 모드: 외부 경로는 IsPathAllowed에서 차단하지 않고 권한 검증 단계로 위임
|
||||
if (AxCopilot.Services.OperationModePolicy.IsInternal(OperationMode))
|
||||
return true; // CheckToolPermissionAsync에서 강제 승인 요청됨
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>경로가 워크스페이스 외부인지 확인합니다.</summary>
|
||||
public bool IsOutsideWorkspace(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(WorkFolder)) return false;
|
||||
try
|
||||
{
|
||||
var fullPath = Path.GetFullPath(path);
|
||||
var workFull = Path.GetFullPath(WorkFolder);
|
||||
return !fullPath.StartsWith(workFull, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
catch { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 파일 경로에 타임스탬프를 추가합니다.
|
||||
/// 예: report.html → report_20260328_1430.html
|
||||
@@ -241,6 +268,32 @@ public class AgentContext
|
||||
|
||||
var effectivePerm = PermissionModeCatalog.NormalizeGlobalMode(GetEffectiveToolPermission(toolName, target));
|
||||
if (PermissionModeCatalog.IsDeny(effectivePerm)) return false;
|
||||
|
||||
// ── 사내 모드 보안 강화: 워크스페이스 외부 경로 접근 시 무조건 사용자 승인 ──
|
||||
// BypassPermissions / AcceptEdits 모드여도 외부 경로는 강제 승인 필요
|
||||
if (AxCopilot.Services.OperationModePolicy.IsInternal(OperationMode)
|
||||
&& !string.IsNullOrWhiteSpace(target)
|
||||
&& IsOutsideWorkspace(target))
|
||||
{
|
||||
if (AskPermission == null) return false;
|
||||
|
||||
var extTarget = target.Trim();
|
||||
var extCacheKey = $"ext_ws|{toolName}|{extTarget}";
|
||||
lock (_permissionLock)
|
||||
{
|
||||
if (_approvedPermissionCache.Contains(extCacheKey))
|
||||
return true;
|
||||
}
|
||||
|
||||
var extAllowed = await AskPermission(toolName, extTarget);
|
||||
if (extAllowed)
|
||||
{
|
||||
lock (_permissionLock)
|
||||
_approvedPermissionCache.Add(extCacheKey);
|
||||
}
|
||||
return extAllowed;
|
||||
}
|
||||
|
||||
if (PermissionModeCatalog.IsAuto(effectivePerm)) return true;
|
||||
if (AskPermission == null) return false;
|
||||
|
||||
@@ -482,4 +535,5 @@ public enum AgentEventType
|
||||
StopRequested, // 중단 요청
|
||||
Paused, // 에이전트 일시정지
|
||||
Resumed, // 에이전트 재개
|
||||
UserMessage, // 실행 중 사용자 메시지 주입
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user