[Phase 17-C] 훅 이벤트 시스템 확장 및 AgentSettingsPanel 훅 UI
AgentLoopService.Skills.cs:
- RunSkillInForkAsync에 PreSkillExecute 훅 발화 추가 (LLM 호출 직전)
- RunSkillInForkAsync에 PostSkillExecute 훅 발화 추가 (응답 수신 후, fire-and-forget)
ChatWindow.WorkFolder.cs:
- SetWorkFolder()에 CwdChanged 훅 발화 추가 (작업 폴더 변경 시, fire-and-forget)
- toolInput=path로 변경된 경로 훅 컨텍스트에 전달
ChatWindow.SlashCommands.cs:
- InjectSlashCommand() 수정: 잘못된 ResolveSlashCommand/SendUserMessageAsync 호출 제거
- ShowSlashChip + SendMessageAsync 올바른 패턴으로 교체 (빌드 오류 4개 수정)
AgentSettingsPanel.xaml / .xaml.cs:
- 훅 이벤트 섹션 추가: ChkExtendedHooks 토글, HookSummaryText, BtnViewHooks, BtnOpenHookSettings
- RefreshHookSummary(): 이벤트 종류별 활성 훅 수 집계 표시
- BtnViewHooks_Click → InjectSlashCommand("/hooks") 연동
- BtnOpenHookSettings_Click → settings.dat 기본 편집기로 열기
빌드: 경고 0, 오류 0
This commit is contained in:
@@ -98,9 +98,17 @@ public partial class AgentLoopService
|
||||
EmitEvent(AgentEventType.Thinking, "skill_fork",
|
||||
$"[Fork] '{skill.Label}' 스킬을 격리 컨텍스트에서 실행 중...");
|
||||
|
||||
// Phase 17-C: PreSkillExecute 훅 발화
|
||||
_ = RunExtendedEventAsync(HookEventKind.PreSkillExecute, forkMessages, ct,
|
||||
toolName: skill.Name, toolInput: preparedBody);
|
||||
|
||||
// 도구 없이 텍스트 응답만 생성 (격리 실행)
|
||||
var response = await _llm.SendAsync(forkMessages, ct);
|
||||
|
||||
// Phase 17-C: PostSkillExecute 훅 발화 (fire-and-forget)
|
||||
_ = RunExtendedEventAsync(HookEventKind.PostSkillExecute, null, CancellationToken.None,
|
||||
toolName: skill.Name, toolOutput: response ?? "");
|
||||
|
||||
// SkillCompleted 이벤트 로그 기록
|
||||
_ = _eventLog?.AppendAsync(AgentEventLogType.SkillCompleted,
|
||||
System.Text.Json.JsonSerializer.Serialize(new
|
||||
|
||||
@@ -411,4 +411,19 @@ public partial class ChatWindow
|
||||
|
||||
return (null, input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 외부에서 슬래시 명령을 입력창에 주입하여 실행합니다.
|
||||
/// AgentSettingsPanel 등에서 /hooks 조회 등에 사용합니다.
|
||||
/// </summary>
|
||||
public void InjectSlashCommand(string slashCmd)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
SlashPopup.IsOpen = false;
|
||||
ShowSlashChip(slashCmd);
|
||||
InputBox.Text = "";
|
||||
_ = SendMessageAsync();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,6 +208,13 @@ public partial class ChatWindow
|
||||
if (recent.Count > maxRecent) recent.RemoveRange(maxRecent, recent.Count - maxRecent);
|
||||
Llm.WorkFolder = path;
|
||||
_settings.Save();
|
||||
|
||||
// Phase 17-C: CwdChanged 훅 발화 (fire-and-forget)
|
||||
_ = _agentLoop.RunExtendedEventAsync(
|
||||
Services.Agent.HookEventKind.CwdChanged,
|
||||
null,
|
||||
CancellationToken.None,
|
||||
toolInput: path);
|
||||
}
|
||||
|
||||
private string GetCurrentWorkFolder()
|
||||
|
||||
@@ -485,6 +485,85 @@
|
||||
Checked="ChkDevMode_Changed" Unchecked="ChkDevMode_Changed"/>
|
||||
</Grid>
|
||||
|
||||
<!-- ─── 훅 설정 (Phase 17-C) ───────────────────────────── -->
|
||||
<Border Height="1" Background="{DynamicResource BorderColor}" Margin="0,6,0,10"/>
|
||||
<Grid Margin="0,0,0,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="11"
|
||||
Foreground="{DynamicResource AccentColor}"
|
||||
VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock Text="훅 이벤트" Foreground="{DynamicResource PrimaryText}"
|
||||
FontSize="12" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<!-- 훅 관리 버튼 → 슬래시 /hooks 명령으로 활성 목록 표시 -->
|
||||
<Border Grid.Column="1" CornerRadius="4" Padding="8,3" Cursor="Hand"
|
||||
Background="#18FFFFFF" MouseLeftButtonUp="BtnViewHooks_Click">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Background" Value="#18FFFFFF"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#28FFFFFF"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<TextBlock Text="/hooks 조회" FontSize="11"
|
||||
Foreground="{DynamicResource AccentColor}"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- 훅 활성화 토글 -->
|
||||
<Grid Margin="0,0,0,6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="확장 훅 활성화" Foreground="{DynamicResource SecondaryText}" FontSize="12"
|
||||
VerticalAlignment="Center"/>
|
||||
<CheckBox x:Name="ChkExtendedHooks" Grid.Column="1"
|
||||
Style="{StaticResource ToggleSwitch}"
|
||||
Checked="ChkExtendedHooks_Changed" Unchecked="ChkExtendedHooks_Changed"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 활성 훅 요약 표시 -->
|
||||
<Border Background="{DynamicResource ItemBackground}" CornerRadius="6"
|
||||
Padding="10,7" Margin="0,0,0,6">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="HookSummaryText"
|
||||
Text="훅 없음 · settings.json의 extended_hooks에서 추가"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 훅 설정 파일 열기 -->
|
||||
<Border CornerRadius="4" Padding="10,5" Cursor="Hand" Margin="0,0,0,4"
|
||||
MouseLeftButtonUp="BtnOpenHookSettings_Click">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Background" Value="#18FFFFFF"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#28FFFFFF"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="11"
|
||||
Foreground="{DynamicResource SecondaryText}"
|
||||
VerticalAlignment="Center" Margin="0,0,5,0"/>
|
||||
<TextBlock Text="settings.json 열기 (훅 편집)"
|
||||
FontSize="11" Foreground="{DynamicResource SecondaryText}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
@@ -115,6 +115,11 @@ public partial class AgentSettingsPanel : UserControl
|
||||
// MCP 서버 목록 표시
|
||||
BuildMcpServerList(settings);
|
||||
|
||||
// Phase 17-C: 훅 UI 초기화
|
||||
if (ChkExtendedHooks != null)
|
||||
ChkExtendedHooks.IsChecked = llm.EnableToolHooks;
|
||||
RefreshHookSummary();
|
||||
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
@@ -356,6 +361,80 @@ public partial class AgentSettingsPanel : UserControl
|
||||
SaveSetting(s => s.Llm.DevMode = ChkDevMode.IsChecked == true);
|
||||
}
|
||||
|
||||
// ── Phase 17-C: 훅 이벤트 UI ─────────────────────────────────────────
|
||||
|
||||
private void ChkExtendedHooks_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isLoading) return;
|
||||
SaveSetting(s => s.Llm.EnableToolHooks = ChkExtendedHooks.IsChecked == true);
|
||||
RefreshHookSummary();
|
||||
}
|
||||
|
||||
private void BtnViewHooks_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
// ChatWindow에서 /hooks 슬래시 명령 실행
|
||||
var chatWin = System.Windows.Application.Current?.Windows
|
||||
.OfType<ChatWindow>()
|
||||
.FirstOrDefault(w => w.IsVisible);
|
||||
chatWin?.InjectSlashCommand("/hooks");
|
||||
}
|
||||
|
||||
private void BtnOpenHookSettings_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
// settings.json 파일을 기본 편집기로 열기
|
||||
var path = System.IO.Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
"AxCopilot", "settings.dat");
|
||||
if (System.IO.File.Exists(path))
|
||||
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(path)
|
||||
{ UseShellExecute = true });
|
||||
}
|
||||
|
||||
private void RefreshHookSummary()
|
||||
{
|
||||
if (HookSummaryText == null) return;
|
||||
|
||||
var settings = CurrentApp?.SettingsService?.Settings;
|
||||
if (settings == null) { HookSummaryText.Text = "설정 로드 실패"; return; }
|
||||
|
||||
if (!settings.Llm.EnableToolHooks)
|
||||
{
|
||||
HookSummaryText.Text = "확장 훅 비활성화됨";
|
||||
return;
|
||||
}
|
||||
|
||||
var hooks = settings.Llm.ExtendedHooks;
|
||||
var counts = new[]
|
||||
{
|
||||
hooks.PreToolUse.Count(h => h.Enabled),
|
||||
hooks.PostToolUse.Count(h => h.Enabled),
|
||||
hooks.PostToolUseFailure.Count(h => h.Enabled),
|
||||
hooks.SessionStart.Count(h => h.Enabled),
|
||||
hooks.SessionEnd.Count(h => h.Enabled),
|
||||
hooks.UserPromptSubmit.Count(h => h.Enabled),
|
||||
hooks.AgentStop.Count(h => h.Enabled),
|
||||
hooks.PreCompact.Count(h => h.Enabled),
|
||||
hooks.PostCompact.Count(h => h.Enabled),
|
||||
hooks.FileChanged.Count(h => h.Enabled),
|
||||
hooks.PermissionRequest.Count(h => h.Enabled),
|
||||
};
|
||||
|
||||
var total = counts.Sum();
|
||||
if (total == 0)
|
||||
{
|
||||
HookSummaryText.Text = "등록된 훅 없음 · settings.json extended_hooks에서 추가";
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = new List<string>();
|
||||
if (hooks.PreToolUse.Any(h => h.Enabled)) parts.Add($"PreTool×{hooks.PreToolUse.Count(h => h.Enabled)}");
|
||||
if (hooks.PostToolUse.Any(h => h.Enabled)) parts.Add($"PostTool×{hooks.PostToolUse.Count(h => h.Enabled)}");
|
||||
if (hooks.SessionStart.Any(h => h.Enabled)) parts.Add($"Session×{hooks.SessionStart.Count(h => h.Enabled)}");
|
||||
if (hooks.UserPromptSubmit.Any(h => h.Enabled)) parts.Add($"Prompt×{hooks.UserPromptSubmit.Count(h => h.Enabled)}");
|
||||
|
||||
HookSummaryText.Text = $"활성 훅 {total}개 · {string.Join(", ", parts)}";
|
||||
}
|
||||
|
||||
// ── Phase 17-A: Reflexion 핸들러 ──────────────────────────────────────
|
||||
|
||||
private void ChkReflexion_Changed(object sender, RoutedEventArgs e)
|
||||
|
||||
Reference in New Issue
Block a user