feat: AI 채팅 패널 (Claude API)

- 백엔드 modules/ai: POST /ai/chat — Anthropic Messages API 프록시(키 서버 env 한정, 30s 타임아웃, throttle 20/분, 미설정 503/외부오류 502)
- ChatDto 검증(role enum, content 8000자, 메시지 50개 상한), 모델 CLAUDE_MODEL(기본 claude-sonnet-4-6)
- 프론트 AgentChatPanel: 우측 슬라이드 채팅(말풍선/타이핑/에러/새 대화), plain text 렌더로 XSS 안전
- AppShell 헤더에 AI 버튼 상시 노출 — 모든 화면에서 열림
- 대화 이력은 stores/ai.store(싱글톤)에 보관 → 화면 이동에도 유지, '새 대화'로만 초기화
- TaskDetailPage 의 기존 목업 에이전트 패널 전부 제거(보안 L1 해소)
- env.example 에 CLAUDE_MODEL 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 00:04:48 +09:00
parent db6a80a6f4
commit 5da3ec8818
15 changed files with 814 additions and 1199 deletions
+42 -1
View File
@@ -7,12 +7,16 @@ import { useRoute, useRouter, RouterLink } from 'vue-router'
import { useAuthStore } from '@/stores/auth.store'
import { useNotificationStore } from '@/stores/notification.store'
import UserAvatar from '@/components/UserAvatar.vue'
import AgentChatPanel from '@/components/AgentChatPanel.vue'
import type { NotificationView } from '@/types/notification'
const route = useRoute()
const router = useRouter()
const authStore = useAuthStore()
// AI 채팅 패널 — 모든 화면 헤더에서 열 수 있다
const agentOpen = ref(false)
// 현재 경로 기준으로 상단 네비 활성 항목을 판별
const activeNav = computed<'tasks' | 'repos'>(() =>
route.path.startsWith('/repos') ? 'repos' : 'tasks',
@@ -86,9 +90,32 @@ async function onLogout() {
</nav>
<div class="topbar-right">
<!-- 화면별 추가 액션(: AI 에이전트 버튼) 끼워 넣는 슬롯 -->
<!-- 화면별 추가 액션을 끼워 넣는 슬롯 -->
<slot name="actions" />
<!-- AI 어시스턴트 모든 화면에서 상시 노출 -->
<button
class="icon-btn agent"
type="button"
title="AI 어시스턴트"
aria-label="AI 어시스턴트"
@click="agentOpen = true"
>
<svg
width="17"
height="17"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 3 13.4 8.6 19 10l-5.6 1.4L12 17l-1.4-5.6L5 10l5.6-1.4Z" />
<path d="M18 16l.7 2.3L21 19l-2.3.7L18 22l-.7-2.3L15 19l2.3-.7Z" />
</svg>
</button>
<button
class="icon-btn"
type="button"
@@ -263,6 +290,12 @@ async function onLogout() {
</header>
<slot />
<!-- AI 채팅 패널 헤더 어디서든 열림 -->
<AgentChatPanel
:open="agentOpen"
@close="agentOpen = false"
/>
</template>
<style scoped>
@@ -348,6 +381,14 @@ async function onLogout() {
background: #f1f2f4;
color: var(--text);
}
/* AI 어시스턴트 버튼 강조 */
.icon-btn.agent {
color: var(--accent);
}
.icon-btn.agent:hover {
background: var(--accent-weak);
color: var(--accent-hover);
}
/* 알림 종 + 드롭다운 */
.noti-wrap {
position: relative;