diff --git a/.astra/project-context/architecture.md b/.astra/project-context/architecture.md index 73c1bb8..a9f29c7 100644 --- a/.astra/project-context/architecture.md +++ b/.astra/project-context/architecture.md @@ -3,15 +3,15 @@ ## Snapshot -- **Workspace**: `ConnectAI` `v2.2.18` _(absolute path varies by environment; resolved from the active VS Code workspace)_ +- **Workspace**: `ConnectAI` `v2.2.19` _(absolute path varies by environment; resolved from the active VS Code workspace)_ - **Description**: The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making. - **Stack**: TypeScript, Node.js, VS Code Extension, LM Studio SDK, Test runner -- **Stats**: 257 source files, ~52,121 lines across 5 top-level modules. +- **Stats**: 265 source files, ~52,981 lines across 5 top-level modules. ## Last Refresh -- **Time**: 2026-05-16T14:34:03.239Z -- **Files newly analysed**: 3 -- **Files reused from cache**: 254 +- **Time**: 2026-05-17T14:50:50.535Z +- **Files newly analysed**: 5 +- **Files reused from cache**: 260 ## Directory Map ```mermaid @@ -37,11 +37,11 @@ mindmap > Arrows: which top-level module imports from which. ```mermaid flowchart LR - src["src/
134 files"] + src["src/
137 files"] media["media/
6 files"] - tests["tests/
33 files"] + tests["tests/
35 files"] core_py["core_py/
6 files"] - docs["docs/
78 files"] + docs["docs/
81 files"] tests --> src ``` @@ -64,10 +64,10 @@ flowchart LR ## Modules -### `src/` — 134 files, ~35,756 lines +### `src/` — 137 files, ~36,157 lines **Sub-directories** -- `src/features/` (61) — Astra Office — public API. 다음 세션에서 추가될 OfficeSnapshot presenter / schema 도 같은 entry 로 노출 예정. 현재 노출: full webview panel H +- `src/features/` (64) — Astra Office — public API. 다음 세션에서 추가될 OfficeSnapshot presenter / schema 도 같은 entry 로 노출 예정. 현재 노출: full webview panel H - `src/core/` (15) — Astra Path Resolver (경로 해결기) Astra의 모든 데이터 파일(.astra 디렉토리)의 경로를 중앙에서 관리합니다. 확장 프로그램의 설치 경로(extensionUri) 기반으로 .astra 디렉토 - `src/memory/` (8) — Episodic Memory (일화 기억) 과거 대화/회의/결정의 맥락 흐름을 저장합니다. 세션 종료 시 자동으로 에피소드를 요약하여 저장합니다. "왜 이렇게 결정했는지", "어떤 흐름으로 진행했는지" 기록. 저장 - `src/retrieval/` (8) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-I @@ -92,32 +92,32 @@ flowchart LR - `src/retrieval/scoring.ts` (518 lines) — Scoring Engine — TF-IDF + Bilingual Tokenizer 단순 includes() 키워드 매칭을 넘어서, TF-IDF 가중치 기반의 문서 스코어링을 제공합니다. 한국어/영어 양국어 토크나이저를 포함합니다. - `src/skills/agentKnowledgeMap.ts` (374 lines) - `src/retrieval/lessonHelpers.ts` (325 lines) — Lesson / Experience Memory — pure helpers (no vscode dependency) "Lesson" = a markdown file in the active brain that captures a past mistake/risk and how to avoid repeating it. Identified by a lessons -- `src/agent.ts` (3579 lines) +- `src/agent.ts` (3633 lines) - `src/features/providers/types.ts` (63 lines) — Cloud LLM provider routing — model id prefix → provider id 매핑. Prefix 규칙: openrouter:anthropic/claude-3.5-sonnet → { provider: 'openrouter', model: 'anthropic/claude-3.5-sonnet' } anthropic:claude-3-5 - `src/lib/engine.ts` (906 lines) - `src/features/company/dispatcher.ts` (1435 lines) — Sequential dispatcher for 1인 기업 모드. Drives one company "turn": user prompt → CEO planner (JSON {brief, tasks}) → for each task in plan: dispatch one specialist (sequentially) - build specialist prompt - `src/features/providers/providerConfig.ts` (78 lines) — Provider 별 API key + enable 토글 저장소. 설계: - API key 자체는 vscode.SecretStorage (secrets) 에 — settings.json / Settings Sync 침범 안 받음. - enabled 토글은 일반 settings (g1nation.providers..enabled) — 사용자가 패널에서 - `src/features/approval/approvalQueue.ts` (129 lines) - `src/integrations/telegram/telegramClient.ts` (154 lines) -- `src/features/astraOffice/view/runtime.ts` (1765 lines) — 자동 분리: src/sidebarProvider.ts 4002-5116 (IIFE 본문) 에서 추출. 동작 동등. ${assets.derivedBase} placeholder 는 panelHtml 에서 .replace() 로 실제 값 주입. 다음 세션에서 OfficeSnapshot 기반으로 단계적으로 잘라낼 예정. +- `src/features/astraOffice/view/runtime.ts` (1887 lines) — 자동 분리: src/sidebarProvider.ts 4002-5116 (IIFE 본문) 에서 추출. 동작 동등. ${assets.derivedBase} placeholder 는 panelHtml 에서 .replace() 로 실제 값 주입. 다음 세션에서 OfficeSnapshot 기반으로 단계적으로 잘라낼 예정. - `src/features/company/agents.ts` (211 lines) — 기본 에이전트 로스터 — 1인 기업 모드의 출고 디폴트. 설계 의도: 소프트웨어/게임 개발 IT 회사의 1인 기업 운영을 가정. 한 사람이 기획 → 디자인 → 개발 → QA → 출시 → 운영/마케팅을 모두 책임질 때 필요한 직군을 빠짐없이 커버하되 역할이 겹치지 않게 분리한다. 직군 구분 (혼동 방지): - 기획자(business) : 무엇을 만들지 정의 - `src/features/company/pixelOfficeState.ts` (286 lines) — Pixel Office — Agent Work Pipeline 상태를 시각화하는 UI Layer 전용 모듈. ─────────────────── 설계 원칙 ─────────────────── 1. Agent 핵심 판단 로직을 절대 바꾸지 않는다. Pipeline 진행, contract 합의, 검수 cycle, 승인 게이트 — 모두 기존 dispatcher - `src/features/company/sessionStore.ts` (231 lines) — Disk persistence for company-mode session artefacts. Each company turn produces a timestamped directory: /.astra/company/sessions/2026-05-13T21-29/ ├─ brief.md ← CEO's task decompositio - `src/features/projectArchitecture/scanner.ts` (644 lines) — Deep static analyser for the Project Architecture Context generator. Walks the project tree (skipping the usual nodemodules / out / dist noise), pulls the role of each interesting file from its leadin - `src/lib/contextManager.ts` (275 lines) — Context Manager (컨텍스트 한계 관리) "context length = 132k" 는 "답변을 132k 토큰까지 생성해도 된다" 가 아닙니다. 시스템 프롬프트 + 대화 기록 + 입력 문서 + 생성될 답변 + 여유분 ≤ context length 이 모듈은 요청을 보내기 전에 입력 토큰을 추정하고, - 동적으로 출력 상한(maxTokens)을 계 -- `src/extension.ts` (1202 lines) +- `src/extension.ts` (1215 lines) -### `media/` — 6 files, ~7,011 lines +### `media/` — 6 files, ~7,243 lines **Key files** -- `media/sidebar.css` (2016 lines) — Stylesheet -- `media/sidebar.js` (3657 lines) +- `media/sidebar.css` (2068 lines) — Stylesheet +- `media/sidebar.js` (3680 lines) - `media/sidebar.html` (546 lines) — Astra +- `media/settings-panel.html` (328 lines) — Astra Settings - `media/settings-panel.css` (210 lines) — Stylesheet -- `media/settings-panel.html` (244 lines) — Astra Settings -- `media/settings-panel.js` (338 lines) +- `media/settings-panel.js` (411 lines) -### `tests/` — 33 files, ~5,811 lines +### `tests/` — 35 files, ~5,969 lines *Depends on*: `src/` **Sub-directories** @@ -160,10 +160,10 @@ flowchart LR - `core_py/optimizer.py` (55 lines) - `core_py/queue_worker.py` (82 lines) -### `docs/` — 78 files, ~3,134 lines +### `docs/` — 81 files, ~3,203 lines **Sub-directories** -- `docs/records/` (65) — Astra Project Chronicle Records +- `docs/records/` (68) — Astra Project Chronicle Records - `docs/docs/` (5) — docs Chronicle Records **Key files** @@ -173,7 +173,7 @@ flowchart LR - `docs/EXPERIENCE_MEMORY_PLAN.md` (122 lines) — Experience Memory (Mistake / Lesson Loop) — Implementation Plan - `docs/records/ConnectAI/development/2026-05-02_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview - `docs/records/ConnectAI/development/2026-05-03_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview -- `docs/records/ConnectAI/timeline.md` (146 lines) — Project Timeline +- `docs/records/ConnectAI/timeline.md` (155 lines) — Project Timeline - `docs/Advanced_Features_Implementation_Guide.md` (40 lines) — Advanced Features Implementation Guide - `docs/PROJECT_CHRONICLE_GUARD_ROADMAP.md` (43 lines) — Project Chronicle Guard: Search Engine Roadmap - `docs/UX_UI_Consistency_Guidelines.md` (44 lines) — UX/UI Consistency Guidelines @@ -196,7 +196,7 @@ flowchart LR ## VS Code Extension Surface - **Extension ID**: `g1nation.astra` - **Activation events**: `onStartupFinished` -- **Commands** (27): +- **Commands** (28): - `g1nation.newChat` — Astra: New Chat - `g1nation.exportChat` — Astra: Export Chat as Markdown - `g1nation.explainSelection` — Astra: Explain Selected Code @@ -224,7 +224,8 @@ flowchart LR - `g1nation.calendar.connect` — Astra: Google Calendar (iCal) 연결 📅 - `g1nation.calendar.refresh` — Astra: Google Calendar 새로고침 📅 - `g1nation.calendar.connectOAuth` — Astra: Google Calendar OAuth 연결 (쓰기) 🔐 -- **Configuration** (62 settings): + - `g1nation.devilAgent.toggle` — Astra: Toggle Devil Agent 🎭 +- **Configuration** (63 settings): - `g1nation.multiAgentEnabled` *(boolean)* _(default: `false`)_ — Enable Multi-Agent Workflow (Planner -> Researcher -> Writer) for complex tasks. - `g1nation.memoryEnabled` *(boolean)* _(default: `true`)_ — Enable layered memory injection before each model response. - `g1nation.memoryShortTermMessages` *(number)* _(default: `8`)_ — Number of recent conversation messages included as short-term memory. @@ -285,7 +286,7 @@ flowchart LR - `g1nation.providers.openrouter.defaultModel` *(string)* _(default: `""`)_ — OpenRouter 의 기본 모델 (예: 'anthropic/claude-3.5-sonnet'). 모델 선택 시 'openrouter:' 형식으로 사이드바 dropdown 에 표시. - `g1nation.providers.anthropic.enabled` *(boolean)* _(default: `false`)_ — Anthropic Claude 직접 API 활성화. OpenRouter 보다 마진 적고 prompt caching 등 native 기능 사용 가능. API key 는 Secret Storage. - `g1nation.providers.anthropic.defaultModel` *(string)* _(default: `"claude-3-5-sonnet-20241022"`)_ — Anthropic 의 기본 모델. 예: 'claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022'. - - _…and 2 more_ + - _…and 3 more_ ## Dependencies - **Runtime** (2): `@lmstudio/sdk`, `pdf-parse` @@ -333,7 +334,7 @@ Astra는 대표님의 명시적인 승인 하에 로컬 시스템의 강력한 **Designed for High-Performance Decision Making.** Copyright (C) **g1nation**. All rights reserved. -_Last auto-scan: 2026-05-16T14:34:03.239Z · signature `857a73ab`_ +_Last auto-scan: 2026-05-17T14:50:50.535Z · signature `5421df2d`_ ## Purpose diff --git a/.astra/project-context/scan-cache.json b/.astra/project-context/scan-cache.json index 96807a6..9de3882 100644 --- a/.astra/project-context/scan-cache.json +++ b/.astra/project-context/scan-cache.json @@ -1,11 +1,11 @@ { "version": 1, - "generatedAt": "2026-05-16T14:34:03.249Z", + "generatedAt": "2026-05-17T14:50:50.547Z", "files": { "src/agent.ts": { - "mtimeMs": 1778941597000, - "size": 205909, - "lines": 3579, + "mtimeMs": 1778943938000, + "size": 209036, + "lines": 3633, "role": "", "imports": [ "src/utils", @@ -260,9 +260,9 @@ "imports": [] }, "src/extension.ts": { - "mtimeMs": 1778938013000, - "size": 62397, - "lines": 1202, + "mtimeMs": 1778943661000, + "size": 63135, + "lines": 1215, "role": "", "imports": [ "src/utils", @@ -292,7 +292,8 @@ "src/retrieval/lessonHelpers", "src/skills/scopedBrainRetriever", "src/integrations/telegram/conversationHistory", - "src/features/calendar" + "src/features/calendar", + "src/features/devilAgent" ] }, "src/features/approval/approvalPanelProvider.ts": { @@ -370,9 +371,9 @@ "imports": [] }, "src/features/astraOffice/view/officeStyles.ts": { - "mtimeMs": 1778938908000, - "size": 23999, - "lines": 401, + "mtimeMs": 1778942662000, + "size": 25747, + "lines": 419, "role": "", "imports": [] }, @@ -388,9 +389,9 @@ ] }, "src/features/astraOffice/view/runtime.ts": { - "mtimeMs": 1778941900000, - "size": 82015, - "lines": 1765, + "mtimeMs": 1779028539000, + "size": 90525, + "lines": 1887, "role": "자동 분리: src/sidebarProvider.ts 4002-5116 (IIFE 본문) 에서 추출. 동작 동등. ${assets.derivedBase} placeholder 는 panelHtml 에서 .replace() 로 실제 값 주입. 다음 세션에서 OfficeSnapshot 기반으로 단계적으로 잘라낼 예정.", "imports": [] }, @@ -650,6 +651,32 @@ "role": "Type definitions for the 1인 기업 (One-Person Company) mode. The mode turns the user into a virtual CEO that dispatches work to a roster of specialist agents. Each turn produces a session directory conta", "imports": [] }, + "src/features/devilAgent/devilPrompt.ts": { + "mtimeMs": 1778943576000, + "size": 3591, + "lines": 71, + "role": "Devil's Advocate (도현) — system prompt 빌더. 설계 원칙: - 모든 약점을 나열하지 않음. 한 turn 에 1개 — 사용자가 깊게 생각하게. - 'X 입장에서 본다면' framing — 본인 의견이 아니라 다른 시점 으로 제시. 이는 LLM 의 '내가 옳다' 경향을 줄이는 잘 알려진 패턴. - 통계 / 구체 수치 / 외부 사례 ", + "imports": [] + }, + "src/features/devilAgent/devilService.ts": { + "mtimeMs": 1778943601000, + "size": 2457, + "lines": 60, + "role": "Devil Agent — 직전 답변에 대한 반박 생성기. 동작: 1. agent.ts 가 main turn 완료 직후 호출 2. 같은 model/engine 으로 별도 LLM call 1회 — 짧은 비판 한 문단 생성 3. 결과를 webview 로 'devilRebuttal' message 로 send 4. 실패 / 비활성 시 silent skip — ma", + "imports": [ + "src/features/devilAgent/devilPrompt" + ] + }, + "src/features/devilAgent/index.ts": { + "mtimeMs": 1778943609000, + "size": 263, + "lines": 13, + "role": "", + "imports": [ + "src/features/devilAgent/devilPrompt", + "src/features/devilAgent/devilService" + ] + }, "src/features/projectArchitecture/index.ts": { "mtimeMs": 1778691699000, "size": 25373, @@ -844,9 +871,9 @@ ] }, "src/features/settings/settingsPanelProvider.ts": { - "mtimeMs": 1778937992000, - "size": 26837, - "lines": 596, + "mtimeMs": 1778943856000, + "size": 29429, + "lines": 646, "role": "", "imports": [ "src/integrations/telegram/telegramClient", @@ -854,7 +881,8 @@ "src/utils", "src/lib/discoverModels", "src/lib/paths", - "src/features/calendar" + "src/features/calendar", + "src/features/providers" ] }, "src/features/sheets/index.ts": { @@ -1354,23 +1382,23 @@ "imports": [] }, "media/settings-panel.html": { - "mtimeMs": 1778937850000, - "size": 12095, - "lines": 244, + "mtimeMs": 1778943830000, + "size": 17362, + "lines": 328, "role": "Astra Settings", "imports": [] }, "media/settings-panel.js": { - "mtimeMs": 1778938082000, - "size": 15189, - "lines": 338, + "mtimeMs": 1778943881000, + "size": 19114, + "lines": 411, "role": "", "imports": [] }, "media/sidebar.css": { - "mtimeMs": 1778934126000, - "size": 86702, - "lines": 2016, + "mtimeMs": 1778943817000, + "size": 88173, + "lines": 2068, "role": "Stylesheet", "imports": [] }, @@ -1382,9 +1410,9 @@ "imports": [] }, "media/sidebar.js": { - "mtimeMs": 1778934151000, - "size": 211710, - "lines": 3657, + "mtimeMs": 1778943769000, + "size": 214022, + "lines": 3680, "role": "", "imports": [] }, @@ -1443,6 +1471,15 @@ "src/core/dataProcessor" ] }, + "tests/devilAgent.test.ts": { + "mtimeMs": 1778943907000, + "size": 3179, + "lines": 61, + "role": "", + "imports": [ + "src/features/devilAgent/devilPrompt" + ] + }, "tests/findBrainFilesCache.test.ts": { "mtimeMs": 1778247665000, "size": 2720, @@ -1573,6 +1610,16 @@ "src/scaffolder/projectScaffolder" ] }, + "tests/providers.test.ts": { + "mtimeMs": 1778942187000, + "size": 4118, + "lines": 97, + "role": "", + "imports": [ + "src/features/providers/types", + "src/features/providers/streamHelpers" + ] + }, "tests/resilience_stress.test.ts": { "mtimeMs": 1777968922000, "size": 6981, @@ -1930,7 +1977,7 @@ "imports": [] }, "docs/records/ConnectAI/chronicle.config.json": { - "mtimeMs": 1778940893000, + "mtimeMs": 1779029444000, "size": 416, "lines": 11, "role": "JSON configuration", @@ -2034,6 +2081,20 @@ "role": "ADR: ASTRA OFFICE 부분 관련해서 개선할 부분이 너무 많아. 개선점이 무었이 있는지 의견 주면 좋겠어.", "imports": [] }, + "docs/records/ConnectAI/decisions/ADR-0015-너의-생각은-사용자가-다음-행동을-추측-안-해도-되는-astra-이게-가장-큰-효과-있을-거-같습니다-3개-.md": { + "mtimeMs": 1778943211000, + "size": 2482, + "lines": 19, + "role": "ADR: 너의 생각은? \"사용자가 다음 행동을 추측 안 해도 되는 Astra\" 이게 가장 큰 효과 있을 거 같습니다. 3개 작업으로 묶어볼 수 있어요: ...", + "imports": [] + }, + "docs/records/ConnectAI/decisions/ADR-0016-내가-블로그글-작성시-사용하고-있는-지침서인데-slot1번-기준으로-더-자연스럽고-트랜디하고-구독자에게-자연.md": { + "mtimeMs": 1779029444000, + "size": 2432, + "lines": 19, + "role": "ADR: 내가 블로그글 작성시 사용하고 있는 지침서인데 slot1번 기준으로 더 자연스럽고 트랜디하고 구독자에게 자연스럽게 정보를 전달 할 수 있게 개선...", + "imports": [] + }, "docs/records/ConnectAI/development/2026-05-02_answer-format-readability-tuning.md": { "mtimeMs": 1777808065000, "size": 1534, @@ -2195,6 +2256,13 @@ "role": "Development Log: Astra: Google Calendar OAuth 연결", "imports": [] }, + "docs/records/ConnectAI/development/2026-05-16_astra-office가-아직은-많이-부족한것-같은데-어떻게-하면-트랜디하고-재미있는-볼거리가-될-수-있을지_implementation.md": { + "mtimeMs": 1778942937000, + "size": 1750, + "lines": 22, + "role": "Development Log: astra office가 아직은 많이 부족한것 같은데 어떻게 하면 트랜디하고 재미있는 볼거리가 될 수 있을지. 개선점 줄 수 있어?", + "imports": [] + }, "docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-작업-할-거야.md": { "mtimeMs": 1778690673000, "size": 652, @@ -2273,9 +2341,9 @@ "imports": [] }, "docs/records/ConnectAI/timeline.md": { - "mtimeMs": 1778937290000, - "size": 9336, - "lines": 146, + "mtimeMs": 1779029444000, + "size": 9978, + "lines": 155, "role": "Project Timeline", "imports": [] }, diff --git a/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json b/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json index 4d3dc89..583a86a 100644 --- a/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json +++ b/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json @@ -1,5 +1,5 @@ { "result": "Final report with inconsistencies. This should be long enough to pass validation.", - "createdAt": 1778941922044, + "createdAt": 1778943957572, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json b/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json index a10e2ae..60b225e 100644 --- a/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json +++ b/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json @@ -1,5 +1,5 @@ { "result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", - "createdAt": 1778941922043, + "createdAt": 1778943957562, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json b/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json index aaaed8d..9be2637 100644 --- a/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json +++ b/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json @@ -1,5 +1,5 @@ { "result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", - "createdAt": 1778941922042, + "createdAt": 1778943957561, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json b/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json index 4e8126e..262ef33 100644 --- a/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json +++ b/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json @@ -1,5 +1,5 @@ { - "result": "---\nid: stress_conflict_1778941922031\ndate: 2026-05-16T14:32:02.046Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (11ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (1ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (0ms)\n", - "createdAt": 1778941922046, + "result": "---\nid: stress_conflict_1778943957542\ndate: 2026-05-16T15:05:57.576Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (13ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (6ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (6ms)\n", + "createdAt": 1778943957576, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/missions/stress_conflict_1778941922031.json b/.astra/tests/stress/.astra/missions/stress_conflict_1778943957542.json similarity index 78% rename from .astra/tests/stress/.astra/missions/stress_conflict_1778941922031.json rename to .astra/tests/stress/.astra/missions/stress_conflict_1778943957542.json index 28f1340..c55b9d3 100644 --- a/.astra/tests/stress/.astra/missions/stress_conflict_1778941922031.json +++ b/.astra/tests/stress/.astra/missions/stress_conflict_1778943957542.json @@ -1,8 +1,8 @@ { - "missionId": "stress_conflict_1778941922031", + "missionId": "stress_conflict_1778943957542", "status": "completed", - "startTime": "2026-05-16T14:32:02.031Z", - "totalElapsedMs": 15, + "startTime": "2026-05-16T15:05:57.542Z", + "totalElapsedMs": 35, "results": { "planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", "researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", @@ -16,30 +16,30 @@ { "from": "idle", "to": "planner", - "durationMs": 11, + "durationMs": 13, "message": "전략 수립 중...", - "ts": "2026-05-16T14:32:02.042Z" + "ts": "2026-05-16T15:05:57.555Z" }, { "from": "planner", "to": "researcher", - "durationMs": 1, + "durationMs": 6, "message": "핵심 정보 수집 및 분석 중...", - "ts": "2026-05-16T14:32:02.043Z" + "ts": "2026-05-16T15:05:57.561Z" }, { "from": "researcher", "to": "writer", - "durationMs": 0, + "durationMs": 6, "message": "최종 리포트 작성 및 편집 중...", - "ts": "2026-05-16T14:32:02.043Z" + "ts": "2026-05-16T15:05:57.567Z" }, { "from": "writer", "to": "completed", - "durationMs": 3, + "durationMs": 10, "message": "미션 완료", - "ts": "2026-05-16T14:32:02.046Z" + "ts": "2026-05-16T15:05:57.577Z" } ], "resilienceMetrics": { diff --git a/assets/pixelOffice/derived/astra-bookshelf-slim.png b/assets/pixelOffice/derived/astra-bookshelf-slim.png new file mode 100644 index 0000000..f620a3e Binary files /dev/null and b/assets/pixelOffice/derived/astra-bookshelf-slim.png differ diff --git a/assets/pixelOffice/derived/astra-chair-task.png b/assets/pixelOffice/derived/astra-chair-task.png new file mode 100644 index 0000000..36e0223 Binary files /dev/null and b/assets/pixelOffice/derived/astra-chair-task.png differ diff --git a/assets/pixelOffice/derived/astra-cooler-slim.png b/assets/pixelOffice/derived/astra-cooler-slim.png new file mode 100644 index 0000000..0682ba0 Binary files /dev/null and b/assets/pixelOffice/derived/astra-cooler-slim.png differ diff --git a/assets/pixelOffice/derived/astra-desk-dark.png b/assets/pixelOffice/derived/astra-desk-dark.png new file mode 100644 index 0000000..a0a24a2 Binary files /dev/null and b/assets/pixelOffice/derived/astra-desk-dark.png differ diff --git a/assets/pixelOffice/derived/astra-desk-exec.png b/assets/pixelOffice/derived/astra-desk-exec.png new file mode 100644 index 0000000..61f0b17 Binary files /dev/null and b/assets/pixelOffice/derived/astra-desk-exec.png differ diff --git a/assets/pixelOffice/derived/astra-desk-work.png b/assets/pixelOffice/derived/astra-desk-work.png new file mode 100644 index 0000000..4842524 Binary files /dev/null and b/assets/pixelOffice/derived/astra-desk-work.png differ diff --git a/assets/pixelOffice/derived/astra-plant-corner.png b/assets/pixelOffice/derived/astra-plant-corner.png new file mode 100644 index 0000000..c1a91d0 Binary files /dev/null and b/assets/pixelOffice/derived/astra-plant-corner.png differ diff --git a/assets/pixelOffice/derived/astra-printer-compact.png b/assets/pixelOffice/derived/astra-printer-compact.png new file mode 100644 index 0000000..aeabdba Binary files /dev/null and b/assets/pixelOffice/derived/astra-printer-compact.png differ diff --git a/assets/pixelOffice/derived/astra-storage-low.png b/assets/pixelOffice/derived/astra-storage-low.png new file mode 100644 index 0000000..528dd2a Binary files /dev/null and b/assets/pixelOffice/derived/astra-storage-low.png differ diff --git a/assets/pixelOffice/derived/office-backdrop-astra-v3.png b/assets/pixelOffice/derived/office-backdrop-astra-v3.png new file mode 100644 index 0000000..108ac82 Binary files /dev/null and b/assets/pixelOffice/derived/office-backdrop-astra-v3.png differ diff --git a/docs/records/ConnectAI/chronicle.config.json b/docs/records/ConnectAI/chronicle.config.json index cdedc13..74bb1e7 100644 --- a/docs/records/ConnectAI/chronicle.config.json +++ b/docs/records/ConnectAI/chronicle.config.json @@ -7,5 +7,5 @@ "corePurpose": "", "detailLevel": "standard", "createdAt": "2026-05-13T13:09:33.788Z", - "updatedAt": "2026-05-16T14:14:53.934Z" + "updatedAt": "2026-05-17T14:50:44.364Z" } diff --git a/docs/records/ConnectAI/decisions/ADR-0015-너의-생각은-사용자가-다음-행동을-추측-안-해도-되는-astra-이게-가장-큰-효과-있을-거-같습니다-3개-.md b/docs/records/ConnectAI/decisions/ADR-0015-너의-생각은-사용자가-다음-행동을-추측-안-해도-되는-astra-이게-가장-큰-효과-있을-거-같습니다-3개-.md new file mode 100644 index 0000000..81fae8a --- /dev/null +++ b/docs/records/ConnectAI/decisions/ADR-0015-너의-생각은-사용자가-다음-행동을-추측-안-해도-되는-astra-이게-가장-큰-효과-있을-거-같습니다-3개-.md @@ -0,0 +1,19 @@ +# ADR: 너의 생각은? "사용자가 다음 행동을 추측 안 해도 되는 Astra" 이게 가장 큰 효과 있을 거 같습니다. 3개 작업으로 묶어볼 수 있어요: ... + +## Status +accepted + +## Context +너의 생각은? "사용자가 다음 행동을 추측 안 해도 되는 Astra" 이게 가장 큰 효과 있을 거 같습니다. 3개 작업으로 묶어볼 수 있어요: 첫 화면 "오늘의 추천" 카드 — Astra 사이드바 열 때 빈 chat 대신 "최근 작업 / 다가오는 마감 / 어제 멈춘 곳 / 추천 다음 작업" 4-카드 dashboard 사용자 정의 파이프라인 UI — Pipeline editor 가 이미 있지만 발견 어려움. "내 자주 쓰는 패턴 만들기" 메뉴 부각 "악마의 변호인" agent 추가 — company roster 에 devil agent 한 명 추가, 의도적 반대 의견 emit. Self-Reflector 와 다른 점은 대화 형식 으로 토론 각 1-2 시간 분량. 셋 다 기존 인프라 위에서 동작 — 새 의존성 없음. 트레이드오프 위 제안 문서를 그대로 따라가면 6개월짜리 product 재정의가 됩니다. Astra 의 핵심 강점 (로컬 LLM, privacy, VS Code 통합) 을 잃... + +## Decision +**결론부터 말씀드리자면, 방향성 자체는 매우 정확합니다.** 현재의 핵심 강점(로컬 LLM, 프라이버시)을 잃고 트렌디한 SaaS를 따라 하려 하는 위험성은 반드시 피해야 합니다. 따라서 **(B) 다른 1-2개 골라서 깊게** 가는 것이 맞습니다. 다만, 선택할 기능은 단순히 '트렌디해 보이는 것'이 아니라, Astra의 핵심 USP인 **"프라이버시가 보장되는 환경에서 AI와 함께하는 지능적인 사고 과정(Intelligence Workflow)"**을 극대화할 수 있는 방향으로 재선정해야 합니다. --- ### 📝 간단 요약 및 상세 답변 Astra Office는 화려한 UI 경쟁에 뛰어들기보다, 사용자의 '사고의 깊이'와 '작업의 흐름' 자체를 업그레이드하는 데 집중해야 합니다. 가장 강력한 초기 초점은 **'악마의 변호인(Devil Agent)'**과 **'워크플로우 자동화 매핑'**을 결합하여, 사용자가 스스로 생각하지 못했던 비판적 관점을 강제로 제시받는 경험에 두어야... + +## Reason +Captured automatically because the conversation contained decision-oriented language. + +## Alternatives +Not captured yet. + +## Consequences +- Future prompts should treat this as project context unless the user changes direction. diff --git a/docs/records/ConnectAI/decisions/ADR-0016-내가-블로그글-작성시-사용하고-있는-지침서인데-slot1번-기준으로-더-자연스럽고-트랜디하고-구독자에게-자연.md b/docs/records/ConnectAI/decisions/ADR-0016-내가-블로그글-작성시-사용하고-있는-지침서인데-slot1번-기준으로-더-자연스럽고-트랜디하고-구독자에게-자연.md new file mode 100644 index 0000000..605fb7f --- /dev/null +++ b/docs/records/ConnectAI/decisions/ADR-0016-내가-블로그글-작성시-사용하고-있는-지침서인데-slot1번-기준으로-더-자연스럽고-트랜디하고-구독자에게-자연.md @@ -0,0 +1,19 @@ +# ADR: 내가 블로그글 작성시 사용하고 있는 지침서인데 slot1번 기준으로 더 자연스럽고 트랜디하고 구독자에게 자연스럽게 정보를 전달 할 수 있게 개선... + +## Status +accepted + +## Context +내가 블로그글 작성시 사용하고 있는 지침서인데 slot1번 기준으로 더 자연스럽고 트랜디하고 구독자에게 자연스럽게 정보를 전달 할 수 있게 개선해야할 부분이 있을까? --- 첨부 파일 내용 --- [FILE: seo_workflow_2026-05-17.json] ``` [ { "id": "1", "role": "analyst", "name": "스타일 분석가", "iconKey": "Search", "model": "gemini-3-flash-preview", "prompt": "당신은 독자의 마음을 읽고 진짜 원하는 정보를 찾아내는 조사 전문가입니다. 주제에 대해 사람들이 가장 궁금해하는 점과 실질적으로 도움이 되는 팩트 기반 정보를 파악하십시오.\n\n[지침]\n1. 독자가 이 주제를 찾아본 '진짜 이유'를 3가지로 정리하십시오.\n2. 어려운 개념은 일상적인 비유로 쉽게 풀어서 설명하십시오.\n3. 구체적인 숫자나 실제 사례를 포함해 할루시네이션 없는 사실 기반 정보를 수집하십... + +## Decision +내가 블로그글 작성시 사용하고 있는 지침서인데 slot1번 기준으로 더 자연스럽고 트랜디하고 구독자에게 자연스럽게 정보를 전달 할 수 있게 개선해야할 부분이 있을까? --- 첨부 파일 내용 --- [FILE: seo_workflow_2026-05-17.json] ``` [ { "id": "1", "role": "analyst", "name": "스타일 분석가", "iconKey": "Search", "model": "gemini-3-flash-preview", "prompt": "당신은 독자의 마음을 읽고 진짜 원하는 정보를 찾아내는 조사 전문가입니다. 주제에 대해 사람들이 가장 궁금해하는 점과 실질적으로 도움이 되는 팩트 기반 정보를 파악하십시오.\n\n[지침]\n1. 독자가 이 주제를 찾아본 '진짜 이유'를 3가지로 정리하십시오.\n2. 어려운 개념은 일상적인 비유로 쉽게 풀어서 설명하십시오.\n3. 구체적인 숫자나 실제 사례를 포함해 할루시네이션 없는 사실 기반 정보를 수집하십... + +## Reason +Captured automatically because the conversation contained decision-oriented language. + +## Alternatives +Not captured yet. + +## Consequences +- Future prompts should treat this as project context unless the user changes direction. diff --git a/docs/records/ConnectAI/development/2026-05-16_astra-office가-아직은-많이-부족한것-같은데-어떻게-하면-트랜디하고-재미있는-볼거리가-될-수-있을지_implementation.md b/docs/records/ConnectAI/development/2026-05-16_astra-office가-아직은-많이-부족한것-같은데-어떻게-하면-트랜디하고-재미있는-볼거리가-될-수-있을지_implementation.md new file mode 100644 index 0000000..d9dfda7 --- /dev/null +++ b/docs/records/ConnectAI/development/2026-05-16_astra-office가-아직은-많이-부족한것-같은데-어떻게-하면-트랜디하고-재미있는-볼거리가-될-수-있을지_implementation.md @@ -0,0 +1,22 @@ +# Development Log: astra office가 아직은 많이 부족한것 같은데 어떻게 하면 트랜디하고 재미있는 볼거리가 될 수 있을지. 개선점 줄 수 있어? + +## Purpose +Record the implementation or verification outcome from the current conversation. + +## Implementation Summary +이건 전형적인 '기능은 갖추었지만, 경험이 부족한' 서비스의 문제입니다. 단순히 기능을 추가하는 것보다 **사용자가 플랫폼을 사용하는 과정 자체를 재미있게 만들고, 필요성을 느끼게 하는 방향**으로 접근해야 합니다. Astra Office가 트렌디하고 매력적인 공간이 되려면, '도구(Tool)'로서의 역할 외에 **'경험(Experience)'과 '커뮤니티(Community)'의 중심지**라는 인상을 주어야 합니다. 세 가지 핵심 관점에서 개선점을 제안합니다. --- ### 💡 1. 경험 및 UX 측면 (Trendy & Aesthetic) 현재의 작업 환경을 단순한 문서 편집 공간이 아니라, 개인화된 '디지털 아틀리에'처럼 느껴지게 해야 합니다. * **초개인화 대시보드:** 사용자가 자신의 주력 워크플로우(프로젝트 A용 AI 요약 위젯, B 프로젝트용 To-Do 리스트 등)만 한눈에 볼 수 있도록 모듈형/위젯 기반의 메인 화면을 제공해야 합니다. 마치 Notion이나 Obsidi... + +## Architecture +Captured automatically from the assistant response and active project context. + +## Changed Files +- No explicit changed file list was captured automatically. + +## Dependency Notes +No new dependency note was captured automatically. + +## Bugs +No bugs recorded. + +## Lessons +- Automatic project records should be generated in the background when the turn contains durable project knowledge. diff --git a/docs/records/ConnectAI/timeline.md b/docs/records/ConnectAI/timeline.md index 10a5720..1d5336c 100644 --- a/docs/records/ConnectAI/timeline.md +++ b/docs/records/ConnectAI/timeline.md @@ -144,3 +144,12 @@ ## 2026-05-16 - Auto development record created: development/2026-05-16_astra-google-calendar-oauth-연결_implementation-2.md + +## 2026-05-16 +- Auto development record created: development/2026-05-16_astra-office가-아직은-많이-부족한것-같은데-어떻게-하면-트랜디하고-재미있는-볼거리가-될-수-있을지_implementation.md + +## 2026-05-16 +- Auto decision record created: decisions/ADR-0015-너의-생각은-사용자가-다음-행동을-추측-안-해도-되는-astra-이게-가장-큰-효과-있을-거-같습니다-3개-.md + +## 2026-05-17 +- Auto decision record created: decisions/ADR-0016-내가-블로그글-작성시-사용하고-있는-지침서인데-slot1번-기준으로-더-자연스럽고-트랜디하고-구독자에게-자연.md diff --git a/media/settings-panel.html b/media/settings-panel.html index 9db4dcd..de6e54c 100644 --- a/media/settings-panel.html +++ b/media/settings-panel.html @@ -284,6 +284,18 @@ + +
+

🎭 Devil's Advocate (도현)

+

매 답변 직후 별도 LLM 호출로 *비판적 sparring partner* 가 한 문단 반박. 사용자의 사고를 능동적 방어로 전환. 같은 모델 재사용 (~10-15% 추가 비용).

+
+ +
+
+ 규칙: 한 답변당 약점 1개만, 통계·수치 인용 금지 (환각 차단), 끝에 우려+검증 방법 명시. 명령 팔레트 Astra: Toggle Devil Agent 🎭 로도 토글. +
+
+

고급

diff --git a/media/settings-panel.js b/media/settings-panel.js index 3e0a7d3..d48ea3e 100644 --- a/media/settings-panel.js +++ b/media/settings-panel.js @@ -57,6 +57,20 @@ const googleFeedback = $('googleFeedback'); const googleError = $('googleError'); + // ---- Devil Agent ---- + const devilEnabled = $('devilEnabled'); + + // ---- Cloud LLM Providers ---- + const prOpenrouterEnabled = $('prOpenrouterEnabled'); + const prOpenrouterKey = $('prOpenrouterKey'); + const prOpenrouterDefault = $('prOpenrouterDefault'); + const prAnthropicEnabled = $('prAnthropicEnabled'); + const prAnthropicKey = $('prAnthropicKey'); + const prAnthropicDefault = $('prAnthropicDefault'); + const prGeminiEnabled = $('prGeminiEnabled'); + const prGeminiKey = $('prGeminiKey'); + const prGeminiDefault = $('prGeminiDefault'); + // ---- Banner ---- const bannerError = $('bannerError'); @@ -157,6 +171,43 @@ googleDisconnectBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.disconnect' })); googleIcalRefreshBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.icalRefresh' })); + // ---- Devil Agent listener ---- + devilEnabled.addEventListener('change', (e) => + vscode.postMessage({ type: 'devilAgent.toggle', enabled: e.target.checked }) + ); + + // ---- Cloud LLM Providers listeners ---- + prOpenrouterEnabled.addEventListener('change', (e) => + vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', enabled: e.target.checked }) + ); + document.querySelector('[data-save="providers.openrouter.apiKey"]').addEventListener('click', () => { + vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', apiKey: prOpenrouterKey.value }); + prOpenrouterKey.value = ''; + }); + document.querySelector('[data-save="providers.openrouter.defaultModel"]').addEventListener('click', () => + vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', defaultModel: prOpenrouterDefault.value }) + ); + prAnthropicEnabled.addEventListener('change', (e) => + vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', enabled: e.target.checked }) + ); + document.querySelector('[data-save="providers.anthropic.apiKey"]').addEventListener('click', () => { + vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', apiKey: prAnthropicKey.value }); + prAnthropicKey.value = ''; + }); + document.querySelector('[data-save="providers.anthropic.defaultModel"]').addEventListener('click', () => + vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', defaultModel: prAnthropicDefault.value }) + ); + prGeminiEnabled.addEventListener('change', (e) => + vscode.postMessage({ type: 'providers.update', providerId: 'gemini', enabled: e.target.checked }) + ); + document.querySelector('[data-save="providers.gemini.apiKey"]').addEventListener('click', () => { + vscode.postMessage({ type: 'providers.update', providerId: 'gemini', apiKey: prGeminiKey.value }); + prGeminiKey.value = ''; + }); + document.querySelector('[data-save="providers.gemini.defaultModel"]').addEventListener('click', () => + vscode.postMessage({ type: 'providers.update', providerId: 'gemini', defaultModel: prGeminiDefault.value }) + ); + document.querySelector('[data-save="advanced.ctxSize"]').addEventListener('click', () => vscode.postMessage({ type: 'advanced.update', maxContextSize: Number(advCtxSize.value) }) ); @@ -332,6 +383,28 @@ ? `마지막 새로고침: ${g.lastIcalFetchAt.slice(0, 16).replace('T', ' ')}` : ''; } + + // ---- Devil Agent ---- + if (state.devilAgent) { + devilEnabled.checked = !!state.devilAgent.enabled; + } + + // ---- Cloud LLM Providers ---- + const pr = state.providers; + if (pr) { + // OpenRouter + prOpenrouterEnabled.checked = !!pr.openrouter.enabled; + prOpenrouterKey.placeholder = pr.openrouter.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'sk-or-...'; + setIfNotFocused(prOpenrouterDefault, pr.openrouter.defaultModel); + // Anthropic + prAnthropicEnabled.checked = !!pr.anthropic.enabled; + prAnthropicKey.placeholder = pr.anthropic.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'sk-ant-...'; + setIfNotFocused(prAnthropicDefault, pr.anthropic.defaultModel); + // Gemini + prGeminiEnabled.checked = !!pr.gemini.enabled; + prGeminiKey.placeholder = pr.gemini.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'AIzaSy...'; + setIfNotFocused(prGeminiDefault, pr.gemini.defaultModel); + } } vscode.postMessage({ type: 'ready' }); diff --git a/media/sidebar.css b/media/sidebar.css index 17c8698..7686c00 100644 --- a/media/sidebar.css +++ b/media/sidebar.css @@ -953,6 +953,50 @@ border-top: 1px dashed var(--border); } + /* Devil's Advocate (도현) 반박 카드 — main 답변 직후 한 장씩. + accent 와 구분되도록 보라색 톤 + 마스크 이모지 prefix 로 시선 끌기. */ + .devil-rebuttal-card { + border: 1px solid rgba(167, 139, 250, 0.55); + background: rgba(167, 139, 250, 0.06); + border-radius: 10px; + padding: 10px 12px; + margin: 8px 0; + font-size: 12px; + line-height: 1.55; + color: var(--text); + } + .devil-rebuttal-head { + font-size: 11px; + color: #a78bfa; + margin-bottom: 6px; + letter-spacing: 0.02em; + } + .devil-rebuttal-body { + white-space: pre-wrap; + margin-bottom: 8px; + } + .devil-rebuttal-actions { + display: flex; + gap: 8px; + } + .devil-rebuttal-actions button { + background: rgba(167, 139, 250, 0.16); + border: 1px solid rgba(167, 139, 250, 0.4); + color: #c7b7ff; + padding: 4px 10px; + border-radius: 6px; + font-size: 11px; + cursor: pointer; + } + .devil-rebuttal-actions button.ghost { + background: transparent; + border-color: var(--line); + color: var(--muted); + } + .devil-rebuttal-actions button:hover { + background: rgba(167, 139, 250, 0.28); + } + /* Intent Alignment 카드 — new_task 요청 직후 C-G-C-F 분석 결과를 보여주고 질문 / 확인 버튼을 띄움. 다른 phase 카드보다 살짝 무게감을 주려고 accent 테두리. */ diff --git a/media/sidebar.js b/media/sidebar.js index e8a93c9..4d0c8a2 100644 --- a/media/sidebar.js +++ b/media/sidebar.js @@ -1011,6 +1011,42 @@ }); break; } + case 'devilRebuttal': { + // Devil Agent (도현) 카드. main assistant 답변 직후 chat 하단에 한 장 추가. + // 사용자가 '재반박' 누르면 그 텍스트가 다음 user prompt 로 가서 main turn 한 번 더 돌고, + // 재반박-입장 hint 가 prompt 에 prepend 됨. + const v = msg.value || {}; + const chatEl = document.getElementById('chat'); + if (!chatEl) break; + const card = document.createElement('div'); + card.className = 'devil-rebuttal-card'; + const persona = String(v.persona || '도현'); + const text = String(v.text || ''); + card.innerHTML = ` +
🎭 ${escAttr(persona)}이(가) 반박합니다
+
+
+ + +
`; + // body 는 textContent 로 안전하게. + card.querySelector('.devil-rebuttal-body').textContent = text; + chatEl.appendChild(card); + chatEl.scrollTop = chatEl.scrollHeight; + card.querySelector('.devil-dismiss').addEventListener('click', () => card.remove()); + card.querySelector('.devil-reply').addEventListener('click', () => { + // 입력창에 prefix 채워서 사용자가 자기 반박 입력하게. + const input = document.getElementById('input'); + if (input) { + input.value = `[${persona}의 반박에 답변] `; + input.focus(); + // 커서 끝으로. + try { input.setSelectionRange(input.value.length, input.value.length); } catch {} + } + card.remove(); + }); + break; + } case 'companyAlignmentCard': { // Intent Alignment 카드. kind에 따라 4가지 모드: // - 'auto-proceed' : confidence high → 자동 진행 안내(읽기 전용) diff --git a/package.json b/package.json index 7075f5d..9885cc9 100644 --- a/package.json +++ b/package.json @@ -146,6 +146,10 @@ { "command": "g1nation.calendar.connectOAuth", "title": "Astra: Google Calendar OAuth 연결 (쓰기) 🔐" + }, + { + "command": "g1nation.devilAgent.toggle", + "title": "Astra: Toggle Devil Agent 🎭" } ], "keybindings": [ @@ -557,6 +561,11 @@ "type": "string", "default": "gemini-2.0-flash-exp", "description": "Gemini 의 기본 모델. 예: 'gemini-2.0-flash-exp', 'gemini-1.5-pro', 'gemini-1.5-flash'." + }, + "g1nation.devilAgent.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "**Devil's Advocate (도현) 활성화** — 매 답변 직후 별도 LLM 호출로 *비판적 sparring partner* 가 한 문단 반박. 사용자의 사고를 *수동적 수용 → 능동적 방어* 로 전환시키는 게 목표. Local LLM 동일 모델 재사용, ~10-15% 추가 비용." } } } diff --git a/src/agent.ts b/src/agent.ts index 81cad7d..ee69d48 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -1302,6 +1302,16 @@ export class AgentExecutor { memoryLayers: this._lastRetrievalInfo?.usedMemoryLayers ?? [], note: `continuations=${continuationCount} historyDropped=${reqMessages.length - budgetedHistory.length}`, }); + // ── Devil Agent (도현) — 비활성 시 silent skip. 활성 시 별도 LLM 호출로 반박 카드 emit. ── + // 비동기 — main turn 완료에 영향 없음. 실패해도 main 답변은 보존됨. + void this._maybeEmitDevilRebuttal({ + userPrompt: prompt || '', + assistantAnswer: finalAssistantContent, + baseUrl: ollamaUrl, + modelName: actualModel, + contextLength: ctxLimits.contextLength, + engine, + }); } else { this.webview.postMessage({ type: 'streamChunk', value: finalAssistantContent }); } @@ -2885,6 +2895,62 @@ export class AgentExecutor { * "lock() request could not be registered" error this method is helping * to avoid. */ + /** + * Devil Agent 반박 emit — main turn 완료 직후 호출 (fire-and-forget). + * 비활성 시 즉시 return. 활성 시 별도 LLM 호출 (callNonStreaming 재사용) 로 짧은 비판 생성. + * 성공 시 webview 에 'devilRebuttal' 메시지 전송 → UI 가 카드로 렌더. + */ + private async _maybeEmitDevilRebuttal(opts: { + userPrompt: string; + assistantAnswer: string; + baseUrl: string; + modelName: string; + contextLength: number; + engine: 'lmstudio' | 'ollama'; + }): Promise { + try { + const { isDevilAgentEnabled, generateDevilRebuttal, DEVIL_PERSONA_NAME } = + await import('./features/devilAgent'); + if (!isDevilAgentEnabled()) return; + if (!opts.userPrompt.trim() || !opts.assistantAnswer.trim()) return; + // Local callLLM wrapper — callNonStreaming 재사용 (cloud / local 자동 라우팅). + const callLLM = async (system: string, userMessage: string, maxTokens: number) => { + const r = await this.callNonStreaming({ + baseUrl: opts.baseUrl, + modelName: opts.modelName, + engine: opts.engine, + messages: [ + { role: 'system', content: system }, + { role: 'user', content: userMessage }, + ], + temperature: 0.7, + maxTokens, + contextLength: opts.contextLength, + signal: this.abortController?.signal, + }); + return r.text; + }; + const rebuttal = await generateDevilRebuttal(callLLM, { + userPrompt: opts.userPrompt, + assistantAnswer: opts.assistantAnswer, + }); + if (!rebuttal) return; + this.webview?.postMessage({ + type: 'devilRebuttal', + value: { + persona: DEVIL_PERSONA_NAME, + text: rebuttal, + // 사용자가 '재반박' 누를 때 원래 컨텍스트로 돌아갈 수 있게 stash. + userPrompt: opts.userPrompt, + assistantAnswer: opts.assistantAnswer, + }, + }); + } catch (e: any) { + // Devil 실패는 main 답변에 영향 없음 — silent log. + logInfo('Devil rebuttal skipped.', { error: e?.message ?? String(e) }); + } + } + private async callNonStreaming(params: { baseUrl: string; modelName: string; diff --git a/src/extension.ts b/src/extension.ts index f8a6520..80574c2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -680,6 +680,19 @@ export async function activate(context: vscode.ExtensionContext) { vscode.commands.registerCommand('g1nation.calendar.connectOAuth', async () => { await runConnectGoogleCalendarOAuth(context); }), + // Devil Agent (도현) — 매 답변 직후 비판적 반박. 토글 명령. + vscode.commands.registerCommand('g1nation.devilAgent.toggle', async () => { + const { isDevilAgentEnabled, setDevilAgentEnabled, DEVIL_PERSONA_NAME } = + await import('./features/devilAgent'); + const wasOn = isDevilAgentEnabled(); + await setDevilAgentEnabled(!wasOn); + const nowOn = !wasOn; + vscode.window.showInformationMessage( + nowOn + ? `🎭 ${DEVIL_PERSONA_NAME} 활성화됨 — 이제 매 답변 뒤에 비판적 반박 카드가 떠요.` + : `🎭 ${DEVIL_PERSONA_NAME} 비활성화됨.` + ); + }), ); /** All lesson/playbook/qa-finding cards in the active brain. Uses the brain index for the lesson-kind diff --git a/src/features/astraOffice/view/officeBody.ts b/src/features/astraOffice/view/officeBody.ts index 99877e0..51186c1 100644 --- a/src/features/astraOffice/view/officeBody.ts +++ b/src/features/astraOffice/view/officeBody.ts @@ -17,7 +17,7 @@ export const OFFICE_BODY = `