diff --git a/.astra/project-context/architecture.md b/.astra/project-context/architecture.md index 0496175..09e30db 100644 --- a/.astra/project-context/architecture.md +++ b/.astra/project-context/architecture.md @@ -3,23 +3,23 @@ ## Snapshot -- **Workspace**: `ConnectAI` `v2.0.2` _(absolute path varies by environment; resolved from the active VS Code workspace)_ +- **Workspace**: `ConnectAI` `v2.0.9` _(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**: 184 source files, ~31,651 lines across 5 top-level modules. +- **Stats**: 201 source files, ~35,737 lines across 5 top-level modules. ## Last Refresh -- **Time**: 2026-05-13T13:48:21.458Z -- **Files newly analysed**: 3 -- **Files reused from cache**: 181 +- **Time**: 2026-05-13T16:44:14.023Z +- **Files newly analysed**: 8 +- **Files reused from cache**: 193 ## Directory Map ```mermaid mindmap root((ConnectAI)) src/ - core/ features/ + core/ memory/ retrieval/ docs/ @@ -37,11 +37,11 @@ mindmap > Arrows: which top-level module imports from which. ```mermaid flowchart LR - src["src/
85 files"] + src["src/
100 files"] media["media/
6 files"] tests["tests/
27 files"] core_py["core_py/
6 files"] - docs["docs/
60 files"] + docs["docs/
62 files"] tests --> src ``` @@ -53,78 +53,78 @@ flowchart LR ## Hub Files > Imported by many other files — touching these has wide blast radius. -- `src/utils.ts` — referenced by **37** files -- `src/config.ts` — referenced by **11** files +- `src/utils.ts` — referenced by **43** files +- `src/config.ts` — referenced by **12** files - `src/lib/paths.ts` — referenced by **10** files +- `src/features/company/types.ts` — referenced by **9** files · 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 +- `src/features/company/agents.ts` — referenced by **7** files · The 9-agent roster for 1인 기업 모드. Each entry is a static description — persona, role, specialty — used to build the specialist's system prompt at dispatch time. The set was adopted from Connectorigin's +- `src/skills/agentKnowledgeMap.ts` — referenced by **6** files - `src/lib/engine.ts` — referenced by **6** files -- `src/sidebarProvider.ts` — referenced by **6** files -- `src/retrieval/scoring.ts` — referenced by **6** files · Scoring Engine — TF-IDF + Bilingual Tokenizer 단순 includes() 키워드 매칭을 넘어서, TF-IDF 가중치 기반의 문서 스코어링을 제공합니다. 한국어/영어 양국어 토크나이저를 포함합니다. -- `src/memory/types.ts` — referenced by **6** files · Memory Type Definitions (메모리 타입 정의) Astra의 5-Layer Cognitive Memory System의 모든 타입을 정의합니다. ① Short-Term ② Long-Term ③ Project ④ Procedural ⑤ Episodic -- `src/retrieval/lessonHelpers.ts` — referenced by **5** files · 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/core/services.ts` — referenced by **6** files ## Modules -### `src/` — 85 files, ~20,591 lines +### `src/` — 100 files, ~23,711 lines **Sub-directories** +- `src/features/` (28) — The 9-agent roster for 1인 기업 모드. Each entry is a static description — persona, role, specialty — used to build the speci - `src/core/` (15) — Astra Path Resolver (경로 해결기) Astra의 모든 데이터 파일(.astra 디렉토리)의 경로를 중앙에서 관리합니다. 확장 프로그램의 설치 경로(extensionUri) 기반으로 .astra 디렉토 -- `src/features/` (14) — Project Architecture Context (Feature 2) Builds a markdown document that captures the durable facts about a project — it - `src/memory/` (8) — Episodic Memory (일화 기억) 과거 대화/회의/결정의 맥락 흐름을 저장합니다. 세션 종료 시 자동으로 에피소드를 요약하여 저장합니다. "왜 이렇게 결정했는지", "어떤 흐름으로 진행했는지" 기록. 저장 - `src/retrieval/` (8) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-I - `src/docs/` (6) — src Chronicle Records - `src/lib/` (6) — Context Manager (컨텍스트 한계 관리) "context length = 132k" 는 "답변을 132k 토큰까지 생성해도 된다" 가 아닙니다. 시스템 프롬프트 + 대화 기록 + 입력 문서 + 생성될 답변 +- `src/integrations/` (4) — Per-chat conversation history for the Telegram bot. Why this exists: the previous bot was stateless — every inbound mess - `src/lmstudio/` (4) — 4 files (.ts) - `src/sidebar/` (4) — 4 files (.ts) - `src/skills/` (4) — 4 files (.ts) -- `src/integrations/` (3) — Subset of the Telegram Bot API types we actually consume. Source: https://core.telegram.org/bots/api Only fields the bot - `src/agents/` (2) — 2 files (.ts) - `src/scaffolder/` (2) — Scaffolder template catalog. Templates are pure data — (projectName) => { [relativePath]: contents }. New templates are **Key files** - `src/utils.ts` (268 lines) -- `src/config.ts` (209 lines) +- `src/config.ts` (216 lines) +- `src/features/company/types.ts` (150 lines) — 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 - `src/lib/paths.ts` (151 lines) -- `src/sidebarProvider.ts` (2603 lines) +- `src/sidebarProvider.ts` (2934 lines) +- `src/features/company/agents.ts` (136 lines) — The 9-agent roster for 1인 기업 모드. Each entry is a static description — persona, role, specialty — used to build the specialist's system prompt at dispatch time. The set was adopted from Connectorigin's - `src/memory/types.ts` (126 lines) — Memory Type Definitions (메모리 타입 정의) Astra의 5-Layer Cognitive Memory System의 모든 타입을 정의합니다. ① Short-Term ② Long-Term ③ Project ④ Procedural ⑤ Episodic - `src/retrieval/scoring.ts` (518 lines) — Scoring Engine — TF-IDF + Bilingual Tokenizer 단순 includes() 키워드 매칭을 넘어서, TF-IDF 가중치 기반의 문서 스코어링을 제공합니다. 한국어/영어 양국어 토크나이저를 포함합니다. - `src/skills/agentKnowledgeMap.ts` (374 lines) -- `src/agent.ts` (3207 lines) +- `src/core/services.ts` (164 lines) +- `src/agent.ts` (3232 lines) +- `src/features/company/companyConfig.ts` (330 lines) — State + config plumbing for 1인 기업 모드. Two surfaces: - CompanyState (runtime data: enabled flag, company name, which agents are active, per-agent model overrides). Persisted in VS Code's globalState so - `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/lib/engine.ts` (849 lines) +- `src/lib/engine.ts` (880 lines) - `src/features/approval/approvalQueue.ts` (129 lines) +- `src/integrations/telegram/telegramClient.ts` (154 lines) - `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/core/astraPath.ts` (50 lines) — Astra Path Resolver (경로 해결기) Astra의 모든 데이터 파일(.astra 디렉토리)의 경로를 중앙에서 관리합니다. 확장 프로그램의 설치 경로(extensionUri) 기반으로 .astra 디렉토리를 해결하여, 사용자 프로젝트 루트가 아닌 ConnectAI 패키지 내부에 데이터를 저장합니다. 이 모듈은 AAL(Astra Autonomou +- `src/extension.ts` (945 lines) - `src/features/projectChronicle/types.ts` (118 lines) -- `src/integrations/telegram/telegramClient.ts` (154 lines) - `src/lmstudio/client.ts` (147 lines) - `src/retrieval/brainIndex.ts` (325 lines) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-IDF 점수를 계산했습니다 — 파일 수가 많아지면 그게 병목입니다. 이 모듈은 /.astra/brain-index.json 에 -- `src/extension.ts` (757 lines) -- `src/features/projectArchitecture/index.ts` (515 lines) — Project Architecture Context (Feature 2) Builds a markdown document that captures the durable facts about a project — its purpose, modules, key files, constraints, decisions — so Astra can attach it t -- `src/lmstudio/activityTracker.ts` (19 lines) -- `src/memory/EpisodicMemory.ts` (278 lines) — Episodic Memory (일화 기억) 과거 대화/회의/결정의 맥락 흐름을 저장합니다. 세션 종료 시 자동으로 에피소드를 요약하여 저장합니다. "왜 이렇게 결정했는지", "어떤 흐름으로 진행했는지" 기록. 저장 위치: {brainPath}/memory/episodes/.json -- `src/memory/LongTermMemory.ts` (243 lines) — Long-Term Memory (장기 기억) 사용자의 취향, 프로젝트 목표, 반복 규칙, 과거 결정 사항을 영구적으로 저장하고 관리합니다. 저장 위치: {brainPath}/memory/longterm.json -- `src/memory/ProjectMemory.ts` (212 lines) — Project Memory (프로젝트 기억) 프로젝트별 요구사항, 코드 구조, 아키텍처 결정, 버그 기록 등을 Astra 확장 프로그램 내부에 저장하고 관리합니다. 저장 위치: {ConnectAI}/.astra/projectmemory.json (기존: {projectRoot}/.astra/ → 변경됨) -- `src/retrieval/index.ts` (514 lines) — RetrievalOrchestrator — Unified RAG Pipeline Astra의 모든 검색 소스를 통합 관리하는 오케스트레이터입니다. 검색 흐름: ① Query Planning — 의도 분류 + 검색 전략 결정 ② Parallel Search — Brain + Memory + Project + Episode 동시 검색 ③ Result Fusio +- `src/features/company/promptBuilder.ts` (202 lines) — System-prompt construction for company-mode agents. Each specialist needs a prompt that includes: - Their identity (name, role, specialty) + optional persona. - The action-tag contract (, +- `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 -### `media/` — 6 files, ~3,304 lines +### `media/` — 6 files, ~4,099 lines **Key files** -- `media/sidebar.css` (987 lines) — Stylesheet -- `media/sidebar.js` (1388 lines) +- `media/sidebar.css` (1225 lines) — Stylesheet +- `media/sidebar.js` (1874 lines) +- `media/sidebar.html` (356 lines) — Astra - `media/settings-panel.css` (210 lines) — Stylesheet -- `media/sidebar.html` (285 lines) — Astra - `media/settings-panel.html` (164 lines) — Astra Settings - `media/settings-panel.js` (270 lines) -### `tests/` — 27 files, ~4,802 lines +### `tests/` — 27 files, ~4,932 lines *Depends on*: `src/` **Sub-directories** - `tests/mocks/` (1) — 1 files (.js) **Key files** -- `tests/agentEngine.test.ts` (646 lines) — AgentEngine Integration Tests & Performance Benchmarks 검증 대상: 1. ErrorClassifier — 오류 유형(Transient/Permanent/Abort) 자동 분류 2. ErrorRecoveryMatrix — 각 규칙이 의도한 대응 전략으로 매핑되는지 검증 3. resilientExecute — 지수 백 +- `tests/agentEngine.test.ts` (776 lines) — AgentEngine Integration Tests & Performance Benchmarks 검증 대상: 1. ErrorClassifier — 오류 유형(Transient/Permanent/Abort) 자동 분류 2. ErrorRecoveryMatrix — 각 규칙이 의도한 대응 전략으로 매핑되는지 검증 3. resilientExecute — 지수 백 - `tests/lmStudioLifecycle.test.ts` (318 lines) — Unit tests for ModelLifecycleManager. Strategy: inject mock ILMStudioClient and a simple in-memory IActivityTracker. No real LM Studio or SDK is touched — the manager file does not import the SDK dire - `tests/telegramBot.test.ts` (363 lines) — Unit tests for TelegramBot + truncateForTelegram. Strategy: - TelegramBot is driven by an injected ITelegramClient stub. We script getUpdates to return queued batches and assert that: - the offset cur - `tests/lmStudioStreamer.test.ts` (220 lines) — Unit tests for LMStudioStreamer. Strategy: inject a fake ILMStudioClient that returns a fake model handle whose respond() yields a controllable async iterable. No real SDK or WebSocket touched. @@ -160,10 +160,10 @@ flowchart LR - `core_py/optimizer.py` (55 lines) - `core_py/queue_worker.py` (82 lines) -### `docs/` — 60 files, ~2,545 lines +### `docs/` — 62 files, ~2,586 lines **Sub-directories** -- `docs/records/` (48) — Astra Project Chronicle Records +- `docs/records/` (50) — Astra Project Chronicle Records - `docs/docs/` (5) — docs Chronicle Records **Key files** @@ -172,7 +172,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` (116 lines) — Project Timeline +- `docs/records/ConnectAI/timeline.md` (122 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** (19): +- **Commands** (23): - `g1nation.newChat` — Astra: New Chat - `g1nation.exportChat` — Astra: Export Chat as Markdown - `g1nation.explainSelection` — Astra: Explain Selected Code @@ -215,8 +215,12 @@ flowchart LR - `g1nation.lesson.manage` — Astra: Browse / Manage Lessons - `g1nation.architecture.refresh` — Astra: Refresh Project Architecture Context - `g1nation.architecture.detach` — Astra: Detach Project Architecture Context + - `g1nation.architecture.attach` — Astra: Attach Project Architecture Context - `g1nation.architecture.open` — Astra: Open Project Architecture Doc -- **Configuration** (38 settings): + - `g1nation.company.toggle` — Astra: Toggle 1인 기업 Mode + - `g1nation.company.manage` — Astra: Manage 1인 기업 Agents + - `g1nation.company.openSessions` — Astra: Open 1인 기업 Sessions Folder +- **Configuration** (39 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. @@ -255,6 +259,7 @@ flowchart LR - `g1nation.embeddingModel` *(string)* _(default: `""`)_ — Embedding model registered in LM Studio / Ollama (e.g. 'text-embedding-bge-small-en-v1.5', 'nomic-embed-text', 'multilingual-e5-small'). When empty, Astra uses TF-IDF only. When set, the brain is embe - `g1nation.embeddingBlendAlpha` *(number)* _(default: `0.5`)_ — Hybrid score blend: 0 = pure TF-IDF (sparse / keyword), 1 = pure embedding cosine (dense / semantic), 0.5 = balanced. Only used when g1nation.embeddingModel is set. Default 0.5. - `g1nation.knowledgeMix.secondBrainWeight` *(number)* _(default: `50`)_ — Knowledge Mix (0–100): how heavily the assistant should lean on Second Brain evidence vs. its own general knowledge. 0 = Second Brain disabled (model knowledge only). 50 = balanced (legacy default). 1 + - `g1nation.enableReflection` *(boolean)* _(default: `true`)_ — Insert a Self-Reflection (Reflector) stage between Researcher and Writer in the multi-agent workflow. The Reflector critically reviews the plan and research output (gaps, contradictions, unsupported c ## Dependencies - **Runtime** (2): `@lmstudio/sdk`, `pdf-parse` @@ -302,7 +307,7 @@ Astra는 대표님의 명시적인 승인 하에 로컬 시스템의 강력한 **Designed for High-Performance Decision Making.** Copyright (C) **g1nation**. All rights reserved. -_Last auto-scan: 2026-05-13T13:48:21.458Z · signature `fefc8c65`_ +_Last auto-scan: 2026-05-13T16:44:14.023Z · signature `c63276ae`_ ## Purpose diff --git a/.astra/project-context/scan-cache.json b/.astra/project-context/scan-cache.json index 70da71d..7b90764 100644 --- a/.astra/project-context/scan-cache.json +++ b/.astra/project-context/scan-cache.json @@ -1,11 +1,11 @@ { "version": 1, - "generatedAt": "2026-05-13T13:48:21.464Z", + "generatedAt": "2026-05-13T16:44:14.033Z", "files": { "src/agent.ts": { - "mtimeMs": 1778677012000, - "size": 184384, - "lines": 3207, + "mtimeMs": 1778683690000, + "size": 185807, + "lines": 3232, "role": "", "imports": [ "src/utils", @@ -38,19 +38,20 @@ ] }, "src/agents/AgentWorkflowManager.ts": { - "mtimeMs": 1777808065000, - "size": 1745, - "lines": 50, + "mtimeMs": 1778690404000, + "size": 2262, + "lines": 60, "role": "", "imports": [ "src/agents/factory", - "src/lib/engine" + "src/lib/engine", + "src/config" ] }, "src/agents/factory.ts": { - "mtimeMs": 1778591282000, - "size": 7965, - "lines": 166, + "mtimeMs": 1778690336000, + "size": 11023, + "lines": 219, "role": "", "imports": [ "src/config", @@ -58,7 +59,7 @@ ] }, "src/bridge.ts": { - "mtimeMs": 1778251262000, + "mtimeMs": 1778681774000, "size": 9705, "lines": 227, "role": "", @@ -71,9 +72,9 @@ ] }, "src/config.ts": { - "mtimeMs": 1778676053000, - "size": 9149, - "lines": 209, + "mtimeMs": 1778690442000, + "size": 9619, + "lines": 216, "role": "", "imports": [] }, @@ -248,9 +249,9 @@ "imports": [] }, "src/extension.ts": { - "mtimeMs": 1778677220000, - "size": 37920, - "lines": 757, + "mtimeMs": 1778687229000, + "size": 49361, + "lines": 945, "role": "", "imports": [ "src/utils", @@ -273,11 +274,13 @@ "src/integrations/telegram/telegramClient", "src/integrations/telegram/telegramBot", "src/core/services", + "src/features/company", "src/features/settings/settingsPanelProvider", "src/skills/agentKnowledgeMap", "src/retrieval", "src/retrieval/lessonHelpers", - "src/skills/scopedBrainRetriever" + "src/skills/scopedBrainRetriever", + "src/integrations/telegram/conversationHistory" ] }, "src/features/approval/approvalPanelProvider.ts": { @@ -308,10 +311,160 @@ "src/features/approval/approvalQueue" ] }, + "src/features/company/agents.ts": { + "mtimeMs": 1778680824000, + "size": 6684, + "lines": 136, + "role": "The 9-agent roster for 1인 기업 모드. Each entry is a static description — persona, role, specialty — used to build the specialist's system prompt at dispatch time. The set was adopted from Connectorigin's", + "imports": [ + "src/features/company/types" + ] + }, + "src/features/company/ceoPlanner.ts": { + "mtimeMs": 1778681095000, + "size": 8406, + "lines": 219, + "role": "CEO planner — turns a user prompt into a CompanyTaskPlan. Lifecycle of one planner call: 1. Build the planner system prompt (template + active-agent list). 2. Hit the AI service with the user prompt a", + "imports": [ + "src/core/services", + "src/utils", + "src/features/company/agents", + "src/features/company/companyConfig", + "src/features/company/promptAssets", + "src/features/company/promptBuilder", + "src/features/company/types" + ] + }, + "src/features/company/ceoReporter.ts": { + "mtimeMs": 1778681122000, + "size": 4812, + "lines": 120, + "role": "CEO synthesis pass — runs after all specialists have finished. Given the per-agent outputs, this asks the CEO model to produce the final markdown report (✅ 완료 / 🚀 다음 / 💡 인사이트) that the user actually", + "imports": [ + "src/core/services", + "src/utils", + "src/features/company/agents", + "src/features/company/promptAssets", + "src/features/company/types" + ] + }, + "src/features/company/companyConfig.ts": { + "mtimeMs": 1778686762000, + "size": 13473, + "lines": 330, + "role": "State + config plumbing for 1인 기업 모드. Two surfaces: - CompanyState (runtime data: enabled flag, company name, which agents are active, per-agent model overrides). Persisted in VS Code's globalState so", + "imports": [ + "src/features/company/agents", + "src/features/company/types" + ] + }, + "src/features/company/dispatcher.ts": { + "mtimeMs": 1778686839000, + "size": 20029, + "lines": 434, + "role": "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", + "imports": [ + "src/core/services", + "src/utils", + "src/skills/scopedBrainRetriever", + "src/skills/agentKnowledgeMap", + "src/retrieval/knowledgeMix", + "src/features/company/agents", + "src/features/company/companyConfig", + "src/features/company/ceoPlanner", + "src/features/company/ceoReporter", + "src/features/company/promptBuilder", + "src/features/company/sessionStore", + "src/features/company/telegramReport", + "src/features/company/types" + ] + }, + "src/features/company/index.ts": { + "mtimeMs": 1778686769000, + "size": 1114, + "lines": 55, + "role": "Public API for 1인 기업 모드. Consumers (sidebarProvider, chatHandlers, command handlers) import from this barrel so internal layout can move around without touching every call site.", + "imports": [ + "src/features/company/agents", + "src/features/company/companyConfig", + "src/features/company/types", + "src/features/company/dispatcher", + "src/features/company/sessionStore" + ] + }, + "src/features/company/promptAssets.ts": { + "mtimeMs": 1778680887000, + "size": 6782, + "lines": 114, + "role": "Inlined prompt assets for the 1인 기업 mode. The CEO planner / reporter / casual-chat prompts are kept as TS string constants rather than loaded from prompts/.md at runtime, for two reasons: 1. Bundling.", + "imports": [] + }, + "src/features/company/promptBuilder.ts": { + "mtimeMs": 1778686868000, + "size": 10317, + "lines": 202, + "role": "System-prompt construction for company-mode agents. Each specialist needs a prompt that includes: - Their identity (name, role, specialty) + optional persona. - The action-tag contract (, ", + "imports": [ + "src/features/company/agents", + "src/features/company/companyConfig", + "src/features/company/types" + ] + }, + "src/features/company/prompts/ceo-chat.md": { + "mtimeMs": 1778680831000, + "size": 462, + "lines": 4, + "role": "당신은 {{COMPANY}}의 CEO입니다. 사용자(사장님)와 짧게 인사·안부·잡담을 주고받습니다. - 한국어로 1~3문장. 친근하지만 사장-CEO 관계는 유지. - 인사·안부 질문이면 자연스럽게 응답하세요. 작업 지시가 아니면 굳이 작업 분배 제안 X. - 회사 정체성·최근 결정·추적기 상태가 컨텍스트에 있으면 자연스럽게 활용. - JSON 출력 금지. ", + "imports": [] + }, + "src/features/company/prompts/ceo-planner.md": { + "mtimeMs": 1778680831000, + "size": 3199, + "lines": 38, + "role": "당신은 \"{{COMPANY}}\"의 CEO입니다. 1인 AI 기업의 사령관이자 오케스트레이터입니다.", + "imports": [] + }, + "src/features/company/prompts/ceo-report.md": { + "mtimeMs": 1778680831000, + "size": 1373, + "lines": 21, + "role": "당신은 {{COMPANY}}의 CEO입니다. 방금 팀이 작업을 끝냈습니다. 각 에이전트의 산출물을 읽고 사장님께 올릴 종합 보고서를 작성하세요.", + "imports": [] + }, + "src/features/company/sessionStore.ts": { + "mtimeMs": 1778680971000, + "size": 8727, + "lines": 231, + "role": "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", + "imports": [ + "src/utils", + "src/features/company/types" + ] + }, + "src/features/company/telegramReport.ts": { + "mtimeMs": 1778686162000, + "size": 8111, + "lines": 168, + "role": "Telegram mirror for the secretary agent (영숙). After every company turn finishes, this helper takes the CEO synthesis + task list and pushes it to the user's Telegram chat — same behaviour as Connector", + "imports": [ + "src/utils", + "src/integrations/telegram/telegramClient", + "src/integrations/telegram/conversationHistory", + "src/features/company/agents", + "src/features/company/types" + ] + }, + "src/features/company/types.ts": { + "mtimeMs": 1778686714000, + "size": 6454, + "lines": 150, + "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/projectArchitecture/index.ts": { - "mtimeMs": 1778679015000, - "size": 22657, - "lines": 515, + "mtimeMs": 1778684164000, + "size": 23147, + "lines": 523, "role": "Project Architecture Context (Feature 2) Builds a markdown document that captures the durable facts about a project — its purpose, modules, key files, constraints, decisions — so Astra can attach it t", "imports": [ "src/utils", @@ -413,6 +566,15 @@ "src/lib/paths" ] }, + "src/integrations/telegram/conversationHistory.ts": { + "mtimeMs": 1778684811000, + "size": 6273, + "lines": 154, + "role": "Per-chat conversation history for the Telegram bot. Why this exists: the previous bot was stateless — every inbound message hit AIService.chat({system, user}) in isolation, with no memory of what the ", + "imports": [ + "src/utils" + ] + }, "src/integrations/telegram/telegramBot.ts": { "mtimeMs": 1778421270000, "size": 11344, @@ -467,9 +629,9 @@ ] }, "src/lib/engine.ts": { - "mtimeMs": 1777985699000, - "size": 38497, - "lines": 849, + "mtimeMs": 1778690608000, + "size": 40830, + "lines": 880, "role": "", "imports": [ "src/core/lock", @@ -740,14 +902,15 @@ ] }, "src/sidebar/chatHandlers.ts": { - "mtimeMs": 1778677129000, - "size": 8596, - "lines": 175, + "mtimeMs": 1778686906000, + "size": 13454, + "lines": 266, "role": "", "imports": [ "src/sidebarProvider", "src/utils", - "src/lib/paths" + "src/lib/paths", + "src/features/company" ] }, "src/sidebar/chronicleHandlers.ts": { @@ -760,9 +923,9 @@ ] }, "src/sidebarProvider.ts": { - "mtimeMs": 1778679027000, - "size": 112397, - "lines": 2603, + "mtimeMs": 1778686896000, + "size": 127185, + "lines": 2934, "role": "", "imports": [ "src/utils", @@ -780,7 +943,9 @@ "src/lib/contextManager", "src/skills/externalSkillLoader", "src/features/projectArchitecture", - "src/features/projectArchitecture/intentDetector" + "src/features/projectArchitecture/intentDetector", + "src/features/company", + "src/core/services" ] }, "src/skills/agentKnowledgeMap.ts": { @@ -816,7 +981,7 @@ ] }, "src/skills/skillInjectionService.ts": { - "mtimeMs": 1778251221000, + "mtimeMs": 1778681774000, "size": 6276, "lines": 145, "role": "", @@ -870,30 +1035,30 @@ "imports": [] }, "media/sidebar.css": { - "mtimeMs": 1778677793000, - "size": 38372, - "lines": 987, + "mtimeMs": 1778688155000, + "size": 49347, + "lines": 1225, "role": "Stylesheet", "imports": [] }, "media/sidebar.html": { - "mtimeMs": 1778677855000, - "size": 16364, - "lines": 285, + "mtimeMs": 1778687548000, + "size": 20499, + "lines": 356, "role": "Astra", "imports": [] }, "media/sidebar.js": { - "mtimeMs": 1778677844000, - "size": 77309, - "lines": 1388, + "mtimeMs": 1778688191000, + "size": 103898, + "lines": 1874, "role": "", "imports": [] }, "tests/agentEngine.test.ts": { - "mtimeMs": 1777983087000, - "size": 27182, - "lines": 646, + "mtimeMs": 1778690526000, + "size": 33414, + "lines": 776, "role": "AgentEngine Integration Tests & Performance Benchmarks 검증 대상: 1. ErrorClassifier — 오류 유형(Transient/Permanent/Abort) 자동 분류 2. ErrorRecoveryMatrix — 각 규칙이 의도한 대응 전략으로 매핑되는지 검증 3. resilientExecute — 지수 백", "imports": [ "src/lib/engine" @@ -1083,7 +1248,7 @@ ] }, "tests/skillInjectionService.test.ts": { - "mtimeMs": 1778251292000, + "mtimeMs": 1778681774000, "size": 6741, "lines": 172, "role": "Unit tests for FileSystemSkillInjectionService. Strategy: drive the service against a real temp directory so path-traversal defenses and writeFileSync paths are exercised end-to-end. The service accep", @@ -1343,7 +1508,7 @@ "imports": [] }, "docs/records/ConnectAI/chronicle.config.json": { - "mtimeMs": 1778680095000, + "mtimeMs": 1778690568000, "size": 416, "lines": 11, "role": "JSON configuration", @@ -1412,6 +1577,13 @@ "role": "ADR: 이 프로젝트의 구조에 대해서 설명해봐.", "imports": [] }, + "docs/records/ConnectAI/decisions/ADR-0010-volumes-data-project-antigravity-connectai-self-reflection-기.md": { + "mtimeMs": 1778689955000, + "size": 1468, + "lines": 19, + "role": "ADR: /Volumes/Data/project/Antigravity/ConnectAI self reflection 기능이 적용되었는지 확인해줘. 1인 ...", + "imports": [] + }, "docs/records/ConnectAI/development/2026-05-02_answer-format-readability-tuning.md": { "mtimeMs": 1777808065000, "size": 1534, @@ -1552,6 +1724,13 @@ "role": "Discussion: /Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 작업할거야", "imports": [] }, + "docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-하위-폴더-포함해서.md": { + "mtimeMs": 1778689036000, + "size": 720, + "lines": 16, + "role": "Discussion: /Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 하위 폴더 포함해서 딥 리서치 해줘. 최적화 할 부분...", + "imports": [] + }, "docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트를-작업할거야.md": { "mtimeMs": 1778677791000, "size": 719, @@ -1588,9 +1767,9 @@ "imports": [] }, "docs/records/ConnectAI/timeline.md": { - "mtimeMs": 1778680095000, - "size": 7871, - "lines": 116, + "mtimeMs": 1778689955000, + "size": 8165, + "lines": 122, "role": "Project Timeline", "imports": [] }, diff --git a/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json b/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json index b207436..f66cd13 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": 1778688441641, + "createdAt": 1778690820030, "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 debbab6..557cd72 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": 1778688441640, + "createdAt": 1778690820030, "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 63b0d80..836ebf2 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": 1778688441639, + "createdAt": 1778690820029, "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 cdddf8a..e9316f9 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_1778688441629\ndate: 2026-05-13T16:07:21.641Z\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]** 전략 수립 중... (10ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (1ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (0ms)\n", - "createdAt": 1778688441641, + "result": "---\nid: stress_conflict_1778690820018\ndate: 2026-05-13T16:47:00.031Z\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]** 핵심 정보 수집 및 분석 중... (0ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (1ms)\n", + "createdAt": 1778690820031, "modelVersion": "unknown" } \ No newline at end of file diff --git a/.astra/tests/stress/.astra/missions/stress_conflict_1778688441629.json b/.astra/tests/stress/.astra/missions/stress_conflict_1778690820018.json similarity index 82% rename from .astra/tests/stress/.astra/missions/stress_conflict_1778688441629.json rename to .astra/tests/stress/.astra/missions/stress_conflict_1778690820018.json index 3639589..2099118 100644 --- a/.astra/tests/stress/.astra/missions/stress_conflict_1778688441629.json +++ b/.astra/tests/stress/.astra/missions/stress_conflict_1778690820018.json @@ -1,8 +1,8 @@ { - "missionId": "stress_conflict_1778688441629", + "missionId": "stress_conflict_1778690820018", "status": "completed", - "startTime": "2026-05-13T16:07:21.629Z", - "totalElapsedMs": 12, + "startTime": "2026-05-13T16:47:00.018Z", + "totalElapsedMs": 13, "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": 10, + "durationMs": 11, "message": "전략 수립 중...", - "ts": "2026-05-13T16:07:21.639Z" + "ts": "2026-05-13T16:47:00.029Z" }, { "from": "planner", "to": "researcher", - "durationMs": 1, + "durationMs": 0, "message": "핵심 정보 수집 및 분석 중...", - "ts": "2026-05-13T16:07:21.640Z" + "ts": "2026-05-13T16:47:00.029Z" }, { "from": "researcher", "to": "writer", - "durationMs": 0, + "durationMs": 1, "message": "최종 리포트 작성 및 편집 중...", - "ts": "2026-05-13T16:07:21.640Z" + "ts": "2026-05-13T16:47:00.030Z" }, { "from": "writer", "to": "completed", "durationMs": 1, "message": "미션 완료", - "ts": "2026-05-13T16:07:21.641Z" + "ts": "2026-05-13T16:47:00.031Z" } ], "resilienceMetrics": { diff --git a/docs/records/ConnectAI/chronicle.config.json b/docs/records/ConnectAI/chronicle.config.json index 26df7bd..2111806 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-13T13:48:15.283Z" + "updatedAt": "2026-05-13T16:44:33.835Z" } diff --git a/docs/records/ConnectAI/decisions/ADR-0010-volumes-data-project-antigravity-connectai-self-reflection-기.md b/docs/records/ConnectAI/decisions/ADR-0010-volumes-data-project-antigravity-connectai-self-reflection-기.md new file mode 100644 index 0000000..0413842 --- /dev/null +++ b/docs/records/ConnectAI/decisions/ADR-0010-volumes-data-project-antigravity-connectai-self-reflection-기.md @@ -0,0 +1,19 @@ +# ADR: /Volumes/Data/project/Antigravity/ConnectAI self reflection 기능이 적용되었는지 확인해줘. 1인 ... + +## Status +accepted + +## Context +/Volumes/Data/project/Antigravity/ConnectAI self reflection 기능이 적용되었는지 확인해줘. 1인 기업 기능에. + +## Decision +확인했습니다. 현재 제공된 프로젝트 기록과 아키텍처 문서를 바탕으로 `/Volumes/Data/project/Antigravity/ConnectAI` 내의 '자체 성찰(Self-Reflection)' 기능 적용 여부를 분석하겠습니다. **결론부터 말씀드리자면, 시스템 설계 상으로는 자체 성찰 및 피드백 루프를 위한 강력한 기반이 구축되어 있습니다.** 하지만 실제 실행 환경에서 이 기능이 완벽하게 작동하고 있는지에 대한 **실행 결과(Execution Trace)**는 별도로 확인해야 합니다. ### 상세 분석 1. **아키텍처적 기반 (Foundation):** * `AgentEngine Architecture Document`에 따르면, 시스템은 Planner → Researcher → Writer의 3단계 멀티 에이전트 워크플로우를 통해 요청을 분할 처리합니다. 이는 단순 실행을 넘어 **메타인지(Meta-cognition)**와 **결과 검증** 단계가 필요함을 내포하고 있습... + +## 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/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-작업-할-거야.md b/docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-작업-할-거야.md new file mode 100644 index 0000000..e9d5534 --- /dev/null +++ b/docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-작업-할-거야.md @@ -0,0 +1,16 @@ +# Discussion: /Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 작업 할 거야. + +## User Request Summary +/Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 작업 할 거야. + +## Interpreted Intent +Capture a meaningful project discussion automatically instead of requiring manual record selection. + +## Questions +No explicit question was captured. + +## Main Discussion +- 프로젝트를 시작하시려는군요. 해당 경로를 기준으로 작업을 진행하겠습니다. 어떤 부분부터 논의하거나 어떤 목표를 가지고 시작하고 싶으신지 말씀해 주십시오. + +## Decisions +No decisions captured yet. diff --git a/docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-하위-폴더-포함해서.md b/docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-하위-폴더-포함해서.md new file mode 100644 index 0000000..9955765 --- /dev/null +++ b/docs/records/ConnectAI/discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-하위-폴더-포함해서.md @@ -0,0 +1,16 @@ +# Discussion: /Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 하위 폴더 포함해서 딥 리서치 해줘. 최적화 할 부분... + +## User Request Summary +/Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 하위 폴더 포함해서 딥 리서치 해줘. 최적화 할 부분이 뭐가 더 있을지. + +## Interpreted Intent +Capture a meaningful project discussion automatically instead of requiring manual record selection. + +## Questions +No explicit question was captured. + +## Main Discussion +- /Volumes/Data/project/Antigravity/ConnectAI 이 프로젝트 하위 폴더 포함해서 딥 리서치 해줘. 최적화 할 부분이 뭐가 더 있을지. + +## Decisions +No decisions captured yet. diff --git a/docs/records/ConnectAI/timeline.md b/docs/records/ConnectAI/timeline.md index f63f7fd..59f067e 100644 --- a/docs/records/ConnectAI/timeline.md +++ b/docs/records/ConnectAI/timeline.md @@ -114,3 +114,12 @@ ## 2026-05-13 - Auto discussion record created: discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-작업할거야.md + +## 2026-05-13 +- Auto discussion record created: discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-하위-폴더-포함해서.md + +## 2026-05-13 +- Auto decision record created: decisions/ADR-0010-volumes-data-project-antigravity-connectai-self-reflection-기.md + +## 2026-05-13 +- Auto discussion record created: discussions/2026-05-13_volumes-data-project-antigravity-connectai-이-프로젝트-작업-할-거야.md diff --git a/package.json b/package.json index 9ab000c..7ca4a8f 100644 --- a/package.json +++ b/package.json @@ -411,6 +411,11 @@ "minimum": 0, "maximum": 100, "description": "Knowledge Mix (0–100): how heavily the assistant should lean on Second Brain evidence vs. its own general knowledge. 0 = Second Brain disabled (model knowledge only). 50 = balanced (legacy default). 100 = Second Brain is the primary evidence; model knowledge only fills harmless background. Per-agent overrides in the Agent Mapping panel win over this global value." + }, + "g1nation.enableReflection": { + "type": "boolean", + "default": true, + "description": "Insert a Self-Reflection (Reflector) stage between Researcher and Writer in the multi-agent workflow. The Reflector critically reviews the plan and research output (gaps, contradictions, unsupported claims, drift from the original objective) and feeds a structured critique to the Writer, which must address it before producing the final report. Reflection failures are non-fatal (the Writer still runs with empty critique). Disable to save one LLM call per mission if you prioritize latency or are running on a very small model." } } } diff --git a/src/agents/AgentWorkflowManager.ts b/src/agents/AgentWorkflowManager.ts index 9e31b1c..79b313f 100644 --- a/src/agents/AgentWorkflowManager.ts +++ b/src/agents/AgentWorkflowManager.ts @@ -1,5 +1,6 @@ -import { PlannerAgent, ResearcherAgent, WriterAgent } from './factory'; -import { AgentEngine, PipelineStage } from '../lib/engine'; +import { PlannerAgent, ResearcherAgent, ReflectorAgent, WriterAgent } from './factory'; +import { AgentEngine, PipelineStage, AgentExecuteOptions } from '../lib/engine'; +import { getConfig } from '../config'; export class AgentWorkflowManager { /** @@ -15,9 +16,16 @@ export class AgentWorkflowManager { const planner = new PlannerAgent(modelName); const researcher = new ResearcherAgent(modelName); const writer = new WriterAgent(modelName); - const engine = new AgentEngine(planner, researcher, writer); + // [Self-Reflection] 설정으로 비활성화하지 않은 경우에만 Reflector를 주입. + const enableReflection = getConfig().enableReflection !== false; + const reflector = enableReflection ? new ReflectorAgent(modelName) : undefined; + const engine = new AgentEngine(planner, researcher, writer, reflector); const missionId = `mission_${Date.now()}`; + const runOptions: AgentExecuteOptions = { + config: { enableReflection } + }; + try { return await engine.runMission( missionId, @@ -26,7 +34,8 @@ export class AgentWorkflowManager { signal, (stage: PipelineStage, message: string) => { onProgress(this.mapStageToUI(stage), message); - } + }, + runOptions ); } catch (error: any) { if (error.name === 'AbortError' || error.message.includes('cancelled')) { @@ -41,6 +50,7 @@ export class AgentWorkflowManager { idle: '대기', planner: 'Planner', researcher: 'Researcher', + reflector: 'Reflector', writer: 'Writer', completed: '완료', error: '오류' diff --git a/src/agents/factory.ts b/src/agents/factory.ts index 797c2b7..2ece225 100644 --- a/src/agents/factory.ts +++ b/src/agents/factory.ts @@ -134,12 +134,13 @@ Your mission is to extract, filter, and synthesize critical data based on a stra } export class WriterAgent extends BaseAgent { - private readonly persona = `You are the [Lead Synthesis Writer & Editor]. + private readonly persona = `You are the [Lead Synthesis Writer & Editor]. Your goal is to produce a state-of-the-art final report that wows the user. - TONE: Authoritative yet accessible. Professional developer/consultant style. - STRUCTURE: Use an executive summary, detailed analysis sections, and a "Final Recommendation" block. - LANGUAGE: Always respond in the user's language (KOREAN). -- POLISHING: Ensure logical flow between sections. Make it look like a premium report.`; +- POLISHING: Ensure logical flow between sections. Make it look like a premium report. +- SELF-CORRECTION: When a [REFLECTION CRITIQUE] block is provided, you MUST address each listed gap, contradiction, or missing-evidence item explicitly before producing the final report. Do not silently ignore the critique.`; async execute(input: string, originalRequest?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise { // [Astra v4.0] Advisor 모드 처리 @@ -154,13 +155,65 @@ Analyze the provided report and suggest 3 high-impact next actions for the user. // Fix 3: Trim input if it's too long (Basic Context Diet) const trimmedData = input.length > 8000 ? input.substring(0, 8000) + '... [Data Trimmed for Performance]' : input; - + const policy = options?.context || ''; + const reflection = options?.priorResults?.reflection; + // Reflector 결과가 있으면 별도 블록으로 주입. 길이 4000자 cap (Writer 입력 비대화 방지). + const reflectionBlock = reflection && reflection.trim().length > 0 + ? `\n5. [REFLECTION CRITIQUE — must be addressed]:\n${reflection.length > 4000 ? reflection.substring(0, 4000) + '... [Critique Trimmed]' : reflection}` + : ''; + const wrappedInput = `### SYSTEM INSTRUCTION: FINAL SYNTHESIS 1. Gathered Research Data: ${trimmedData} 2. User's Original Objective: ${originalRequest} 3. Applied Knowledge & Filtering Policy: ${policy} -4. Mission: Write the definitive final report in KOREAN.`; +4. Mission: Write the definitive final report in KOREAN.${reflectionBlock}`; + return this.callLLM(this.persona, wrappedInput, signal); + } +} + +export class ReflectorAgent extends BaseAgent { + private readonly persona = `You are the [Internal Critic & Self-Reflection Officer]. +Your sole role is META-COGNITION: stress-test the plan and the research output BEFORE the Writer commits to a final report. +- POSTURE: Skeptical, rigorous, blunt. You are looking for what is WRONG, not what is right. +- DO NOT: rewrite the report, add new content, or speculate beyond the evidence provided. +- DO: surface gaps, unsupported claims, contradictions, drift from the original objective, and missing perspectives. +- OUTPUT STRICTLY in this Markdown shape (Korean): + ## 🧭 Alignment with Objective + - <원래 요청 대비 일치/이탈 평가> + ## 🕳 Gaps & Missing Evidence + - + ## ⚖️ Contradictions / Conflicts + - + ## 🚨 Unsupported / Weak Claims + - <근거가 빈약하거나 일반화된 진술> + ## ✅ Guidance for Writer + - +- CONSTRAINT: 최대 500단어. 새 지식을 만들지 말고, 제공된 자료에서만 판단할 것.`; + + async execute(input: string, _context?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise { + const plan = options?.priorResults?.plan || '(plan unavailable)'; + const research = input; + const originalPrompt = options?.priorResults?.originalPrompt || '(original prompt unavailable)'; + const brainContext = options?.context || ''; + + // Reflector 는 중간 단계이므로 비대한 입력을 방지하기 위해 각 섹션을 cap. + const cap = (s: string, n: number) => s.length > n ? s.substring(0, n) + '... [trimmed]' : s; + + const wrappedInput = `### SYSTEM INSTRUCTION: SELF-REFLECTION PASS +1. Original User Objective: +${cap(originalPrompt, 1500)} + +2. Planner Blueprint: +${cap(plan, 3000)} + +3. Researcher Output (to be critiqued): +${cap(research, 5000)} + +4. Knowledge / Brain Context (for cross-check only — do not invent beyond this): +${cap(brainContext, 2000)} + +5. Mission: Run a single rigorous reflection pass and output the structured critique exactly as specified by your persona.`; return this.callLLM(this.persona, wrappedInput, signal); } } diff --git a/src/config.ts b/src/config.ts index 2904bdf..b65a9f3 100644 --- a/src/config.ts +++ b/src/config.ts @@ -67,6 +67,12 @@ export interface IAgentConfig { * Per-agent overrides live in AgentKnowledgeEntry.secondBrainWeight and win. */ knowledgeMixSecondBrainWeight: number; + /** + * [Self-Reflection] Researcher와 Writer 사이에 메타인지 단계(Reflector)를 삽입할지 여부. + * true(기본): Reflector가 plan/research를 비판적으로 검토한 critique을 Writer에 주입. + * false: 기존 3단계(Planner→Researcher→Writer) 그대로 — 1 LLM 호출 절약 (저성능 모델/저지연 우선 시). + */ + enableReflection: boolean; } // ─── 경로 정규화 유틸리티 ─── @@ -153,6 +159,7 @@ export function getConfig(): IAgentConfig { knowledgeMixSecondBrainWeight: Math.max(0, Math.min(100, Math.round( cfg.get('knowledgeMix.secondBrainWeight', 50) ))), + enableReflection: cfg.get('enableReflection', true), }; } diff --git a/src/lib/engine.ts b/src/lib/engine.ts index 0c18299..de73cea 100644 --- a/src/lib/engine.ts +++ b/src/lib/engine.ts @@ -49,7 +49,7 @@ export interface IAgent { /** * 파이프라인 단계 상태 정의 */ -export type PipelineStage = 'idle' | 'planner' | 'researcher' | 'writer' | 'completed' | 'error'; +export type PipelineStage = 'idle' | 'planner' | 'researcher' | 'reflector' | 'writer' | 'completed' | 'error'; /** * 감사(Audit) 이력에 기록되는 단일 상태 전환 엔트리. @@ -449,7 +449,9 @@ export class AgentEngine { constructor( private readonly planner: IAgent, private readonly researcher: IAgent, - private readonly writer: IAgent + private readonly writer: IAgent, + // [Self-Reflection] Researcher와 Writer 사이에 주입되는 메타인지 노드. 미주입 시 기존 3단계 파이프라인을 그대로 유지. + private readonly reflector?: IAgent ) {} /** @@ -528,16 +530,45 @@ export class AgentEngine { // [Structural Fix] 점수가 낮을수록 더 상세한 근거를 요구(comprehensive)하도록 로직 역전 const writerLevel: AbstractionLevel = researchScore < 65 ? 'comprehensive' : 'balanced'; + // --- Phase 3.5: Reflector (Self-Reflection) --- + // Reflector가 주입되어 있고 옵션에서 명시적으로 끄지 않은 경우에만 실행한다. + // 실패해도 파이프라인을 막지 않는다(soft-fail): Reflector는 품질 보강이지 필수 게이트가 아님. + let reflection = ''; + const reflectionDisabled = options?.config?.enableReflection === false; + if (this.reflector && !reflectionDisabled) { + try { + reflection = await this.executeStep( + state, 'reflector', '중간 산출물 자기검증 중...', + () => this.resilientExecute(state, this.reflector!, 'Reflector', research, brainContext, signal, onProgress, { + ...options, + context: brainContext, + signal, + config: { ...options?.config, role: 'reflector', isSamePrompt: true }, + priorResults: { plan, originalPrompt: prompt, ...options?.priorResults }, + abstractionLevel: 'balanced' + }), + // [Cache namespace] Writer와 동일한 (research, prompt) 페어를 쓰면 CacheManager가 + // Writer 호출 시 reflector 결과를 그대로 반환해버린다. 단계명을 prefix로 분리. + `reflector::${research}`, prompt, signal, onProgress + ); + } catch (reflErr: any) { + // Reflector 실패는 치명적이지 않다. 감사 이력에만 남기고 빈 reflection으로 Writer를 진행시킨다. + if (reflErr?.name === 'AbortError') throw reflErr; + logError(`[AgentEngine] Reflector soft-fail — Writer 계속 진행: ${reflErr?.message || reflErr}`); + reflection = ''; + } + } + // --- Phase 4: Writer --- const finalReport = await this.executeStep( state, 'writer', '최종 리포트 작성 및 편집 중...', - () => this.resilientExecute(state, this.writer, 'Writer', research, prompt, signal, onProgress, { + () => this.resilientExecute(state, this.writer, 'Writer', research, prompt, signal, onProgress, { ...options, - context: brainContext, - signal, - config: { role: 'writer', allowFallback: true, isSamePrompt: true, ...options?.config }, - priorResults: { plan, writerPrep, previousValidData: state.getResult('finalReport'), ...options?.priorResults }, - abstractionLevel: writerLevel + context: brainContext, + signal, + config: { role: 'writer', allowFallback: true, isSamePrompt: true, ...options?.config }, + priorResults: { plan, writerPrep, reflection, previousValidData: state.getResult('finalReport'), ...options?.priorResults }, + abstractionLevel: writerLevel }), research, prompt, signal, onProgress ); diff --git a/tests/agentEngine.test.ts b/tests/agentEngine.test.ts index 20c6695..8b9f68b 100644 --- a/tests/agentEngine.test.ts +++ b/tests/agentEngine.test.ts @@ -364,6 +364,142 @@ describe('AgentEngine Integration', () => { }); }); +// ═══════════════════════════════════════════════ +// Test Suite 4b: Self-Reflection Stage +// ═══════════════════════════════════════════════ + +class SpyAgent implements IAgent { + public callCount = 0; + public lastInput: string | undefined; + public lastContext: string | undefined; + public lastOptions: AgentExecuteOptions | undefined; + public calls: { input: string; context?: string; options?: AgentExecuteOptions }[] = []; + constructor(private readonly response: string) {} + async execute(input: string, context?: string, _signal?: AbortSignal, options?: AgentExecuteOptions): Promise { + this.callCount++; + this.lastInput = input; + this.lastContext = context; + this.lastOptions = options; + this.calls.push({ input, context, options }); + return this.response; + } +} + +class ThrowingAgent implements IAgent { + public callCount = 0; + constructor(private readonly message: string = '404: model not found') {} + async execute(): Promise { + this.callCount++; + throw new Error(this.message); + } +} + +describe('AgentEngine — Self-Reflection Stage', () => { + test('Reflector 주입 시 Researcher와 Writer 사이에 1회 실행되며 결과가 Writer.priorResults.reflection 으로 전달되어야 한다', async () => { + const planner = new SpyAgent('Plan: rigorous blueprint covering all objectives.'); + const researcher = new SpyAgent('Research: dense factual synthesis with supporting evidence.'); + const reflector = new SpyAgent('## ✅ Guidance for Writer\n- Add missing risk section.'); + const writer = new SpyAgent('Final report incorporating critique.'); + + const engine = new AgentEngine(planner, researcher, writer, reflector); + const stages: PipelineStage[] = []; + await engine.runMission( + 'reflect_001', 'Test prompt', 'brain ctx', + createAbortSignal(), + (stage) => { stages.push(stage); } + ); + + // 정확히 1회 호출 + expect(reflector.callCount).toBe(1); + // 'reflector' 단계가 onProgress에 등장 + expect(stages).toContain('reflector'); + // Stage 순서: planner → researcher → reflector → writer → completed + const idxResearcher = stages.indexOf('researcher'); + const idxReflector = stages.indexOf('reflector'); + const idxWriter = stages.indexOf('writer'); + expect(idxResearcher).toBeGreaterThanOrEqual(0); + expect(idxReflector).toBeGreaterThan(idxResearcher); + expect(idxWriter).toBeGreaterThan(idxReflector); + + // Reflector 입력: research 결과를 input으로 받고 plan/originalPrompt를 priorResults로 받는다 + expect(reflector.lastInput).toContain('Research: dense factual synthesis'); + expect(reflector.lastOptions?.priorResults?.plan).toContain('Plan: rigorous blueprint'); + expect(reflector.lastOptions?.priorResults?.originalPrompt).toBe('Test prompt'); + + // Writer 는 (1) writer 단계와 (2) Phase5 generateProactiveAdvice(advisor 모드)에서 각각 1회씩 호출된다. + // 첫 번째 호출(메인 writer 단계)이 reflection 을 받았어야 한다. + expect(writer.calls.length).toBeGreaterThanOrEqual(1); + const writerMainCall = writer.calls.find(c => c.options?.config?.role === 'writer'); + expect(writerMainCall).toBeDefined(); + expect(writerMainCall?.options?.priorResults?.reflection).toContain('Guidance for Writer'); + }); + + test('Reflector 미주입 시 기존 3단계 파이프라인이 그대로 동작해야 한다 (역호환성)', async () => { + const writer = new SpyAgent('Final report without reflection.'); + const engine = new AgentEngine( + new SpyAgent('Plan output of sufficient length to pass validation.'), + new SpyAgent('Research output of sufficient length to pass validation.'), + writer + // reflector 미전달 + ); + + const stages: PipelineStage[] = []; + const result = await engine.runMission( + 'reflect_002', 'Test prompt', 'ctx', + createAbortSignal(), + (stage) => { stages.push(stage); } + ); + + expect(result).toContain('Final report without reflection'); + expect(stages).not.toContain('reflector'); + // Writer는 reflection 없이도 동작 (priorResults.reflection은 빈 문자열) + expect(writer.lastOptions?.priorResults?.reflection ?? '').toBe(''); + }); + + test('config.enableReflection=false 옵션으로 Reflector 가 주입돼있어도 스킵되어야 한다', async () => { + const reflector = new SpyAgent('should not be called'); + const writer = new SpyAgent('Final report bypassing reflection.'); + const engine = new AgentEngine( + new SpyAgent('Plan output of sufficient length to pass validation.'), + new SpyAgent('Research output of sufficient length to pass validation.'), + writer, + reflector + ); + + const stages: PipelineStage[] = []; + await engine.runMission( + 'reflect_003', 'Test prompt', 'ctx', + createAbortSignal(), + (stage) => { stages.push(stage); }, + { config: { enableReflection: false } } + ); + + expect(reflector.callCount).toBe(0); + expect(stages).not.toContain('reflector'); + // Writer는 정상 실행되고 reflection은 빈 문자열 + expect(writer.lastOptions?.priorResults?.reflection ?? '').toBe(''); + }); + + test('Reflector 실패는 soft-fail — Writer 가 빈 critique 으로 진행되어 미션이 완료되어야 한다', async () => { + const reflector = new ThrowingAgent('Failed to fetch'); // transient → 재시도 소진 후 throw + const writer = new SpyAgent('Final report despite reflector failure.'); + const engine = new AgentEngine( + new SpyAgent('Plan output of sufficient length to pass validation.'), + new SpyAgent('Research output of sufficient length to pass validation.'), + writer, + reflector + ); + + const result = await engine.runMission( + 'reflect_004', 'Test prompt', 'ctx', createAbortSignal(), noopProgress + ); + + expect(result).toContain('Final report despite reflector failure'); + // Writer는 빈 reflection으로 진행되었어야 함 + expect(writer.lastOptions?.priorResults?.reflection ?? '').toBe(''); + }, 60000); +}); + // ═══════════════════════════════════════════════ // Test Suite 5: Performance Benchmark // ═══════════════════════════════════════════════