diff --git a/.astra/tests/engine/.astra/missions/integration_002.json b/.astra/tests/engine/.astra/missions/integration_002.json deleted file mode 100644 index 9cfa058..0000000 --- a/.astra/tests/engine/.astra/missions/integration_002.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "missionId": "integration_002", - "status": "planner", - "startTime": "2026-05-12T14:23:21.996Z", - "totalElapsedMs": 12, - "results": {}, - "promptHash": "1439da2cb34b7c5b", - "transitionCount": 1, - "transitions": [ - { - "from": "idle", - "to": "planner", - "durationMs": 12, - "message": "전략 수립 중...", - "ts": "2026-05-12T14:23:22.008Z" - } - ], - "resilienceMetrics": { - "fallbacks": 0, - "retries": 0, - "maxConflictScore": 0, - "deduplications": 0 - } -} \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json b/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json new file mode 100644 index 0000000..e18df33 --- /dev/null +++ b/.astra/tests/stress/.astra/cache/259a37934ead3910a8722b82054d46d2ca2057b05c488be1dcf439166ac5a9a1.json @@ -0,0 +1,5 @@ +{ + "result": "Final report with inconsistencies. This should be long enough to pass validation.", + "createdAt": 1778596848199, + "modelVersion": "unknown" +} \ No newline at end of file diff --git a/.astra/tests/stress/.astra/cache/5f56d7c28eda4d8014dd780e2754d34e891fd609323064a5114d3b32b04f003b.json b/.astra/tests/stress/.astra/cache/5f56d7c28eda4d8014dd780e2754d34e891fd609323064a5114d3b32b04f003b.json deleted file mode 100644 index 187e398..0000000 --- a/.astra/tests/stress/.astra/cache/5f56d7c28eda4d8014dd780e2754d34e891fd609323064a5114d3b32b04f003b.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "result": "Plan OK passes validation and meets all length requirements.", - "createdAt": 1778595801894, - "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 new file mode 100644 index 0000000..8d6529a --- /dev/null +++ b/.astra/tests/stress/.astra/cache/65775be352df43297b63c7af59c9f4f39d2bc368f77456c37b5eef9a94a66b5c.json @@ -0,0 +1,5 @@ +{ + "result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", + "createdAt": 1778596848198, + "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 new file mode 100644 index 0000000..f43a6fe --- /dev/null +++ b/.astra/tests/stress/.astra/cache/6894d26c5b0a55d25d756a473225c7a44d7661af673b24e3f49551a7a2e50280.json @@ -0,0 +1,5 @@ +{ + "result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", + "createdAt": 1778596848197, + "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 new file mode 100644 index 0000000..b014709 --- /dev/null +++ b/.astra/tests/stress/.astra/cache/88cb61499f88ed38165b64bd3e8adc543795e4b427b64540a49c9ab27c7fe213.json @@ -0,0 +1,5 @@ +{ + "result": "---\nid: stress_conflict_1778596848186\ndate: 2026-05-12T14:40:48.199Z\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]** 최종 리포트 작성 및 편집 중... (1ms)\n", + "createdAt": 1778596848199, + "modelVersion": "unknown" +} \ No newline at end of file diff --git a/.astra/tests/stress/.astra/missions/stress_conflict_1778596848186.json b/.astra/tests/stress/.astra/missions/stress_conflict_1778596848186.json new file mode 100644 index 0000000..427dba9 --- /dev/null +++ b/.astra/tests/stress/.astra/missions/stress_conflict_1778596848186.json @@ -0,0 +1,51 @@ +{ + "missionId": "stress_conflict_1778596848186", + "status": "completed", + "startTime": "2026-05-12T14:40:48.186Z", + "totalElapsedMs": 13, + "results": { + "planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", + "researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", + "writerPrep": "[Original Prompt] Conflict Test\n[Plan Summary] Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.\n[Brain Context Available] Yes (3 chars)", + "writer": "Final report with inconsistencies. This should be long enough to pass validation.", + "finalReport": "Final report with inconsistencies. This should be long enough to pass validation." + }, + "promptHash": "f5146cb9f9670d2a", + "transitionCount": 4, + "transitions": [ + { + "from": "idle", + "to": "planner", + "durationMs": 11, + "message": "전략 수립 중...", + "ts": "2026-05-12T14:40:48.197Z" + }, + { + "from": "planner", + "to": "researcher", + "durationMs": 1, + "message": "핵심 정보 수집 및 분석 중...", + "ts": "2026-05-12T14:40:48.198Z" + }, + { + "from": "researcher", + "to": "writer", + "durationMs": 1, + "message": "최종 리포트 작성 및 편집 중...", + "ts": "2026-05-12T14:40:48.199Z" + }, + { + "from": "writer", + "to": "completed", + "durationMs": 0, + "message": "미션 완료", + "ts": "2026-05-12T14:40:48.199Z" + } + ], + "resilienceMetrics": { + "fallbacks": 0, + "retries": 0, + "maxConflictScore": 60, + "deduplications": 0 + } +} \ No newline at end of file diff --git a/.astra/tests/stress/.astra/missions/stress_fallback_1778595801883.json b/.astra/tests/stress/.astra/missions/stress_fallback_1778595801883.json deleted file mode 100644 index 1e8b35e..0000000 --- a/.astra/tests/stress/.astra/missions/stress_fallback_1778595801883.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "missionId": "stress_fallback_1778595801883", - "status": "researcher", - "startTime": "2026-05-12T14:23:21.883Z", - "totalElapsedMs": 12, - "results": { - "planner": "Plan OK passes validation and meets all length requirements." - }, - "promptHash": "5c39123805ffb4e2", - "transitionCount": 2, - "transitions": [ - { - "from": "idle", - "to": "planner", - "durationMs": 11, - "message": "전략 수립 중...", - "ts": "2026-05-12T14:23:21.894Z" - }, - { - "from": "planner", - "to": "researcher", - "durationMs": 1, - "message": "핵심 정보 수집 및 분석 중...", - "ts": "2026-05-12T14:23:21.895Z" - } - ], - "resilienceMetrics": { - "fallbacks": 0, - "retries": 0, - "maxConflictScore": 0, - "deduplications": 0 - } -} \ No newline at end of file diff --git a/PATCHNOTES.md b/PATCHNOTES.md index f09d51c..bcbb38c 100644 --- a/PATCHNOTES.md +++ b/PATCHNOTES.md @@ -1,5 +1,14 @@ # Astra Patch Notes +## v2.80.36 (2026-05-12) +### 🎨 UI/UX Refinement & Agent Logic Optimization +- **사이드바 UI 전면 고도화:** `sidebar.html`, `sidebar.js`, `sidebar.css`를 갱신하여 더 매끄러운 애니메이션과 직관적인 컴포넌트 인터랙션을 구현했습니다. +- **에이전트 추론 로직 최적화:** `agent.ts` 내의 컨텍스트 주입 및 응답 생성 파이프라인을 개선하여 더 정확하고 빠른 답변이 가능하도록 조정했습니다. +- **로컬 경로 프리플라이트 강화:** `localPathPreflight.test.ts` 수정을 통해 다양한 작업 환경에서의 경로 인식 안정성을 재검증했습니다. +- **신규 패키징:** `astra-2.80.36.vsix` 패키지를 생성하여 최신 UI 개선 사항과 로직 최적화를 통합했습니다. + +--- + ## v2.80.35 (2026-05-12) ### 🧠 Experience Memory & Architectural Lessons - **경험 메모리(Experience Memory) 설계:** `EXPERIENCE_MEMORY_PLAN.md`를 통해 에이전트가 수행한 작업의 성공/실패 사례를 '레슨'으로 자산화하는 체계를 설계했습니다. diff --git a/media/sidebar.css b/media/sidebar.css index d337956..95bb933 100644 --- a/media/sidebar.css +++ b/media/sidebar.css @@ -755,6 +755,7 @@ .ready-bar .rb-seg.ok { color: var(--success); } .ready-bar .rb-seg.bad { color: var(--error); } .ready-bar .rb-seg.rb-dim, .ready-bar .rb-dim { color: var(--border-bright); } + .ready-bar .rb-seg.rb-warn { color: var(--warning); font-weight: 600; } .ready-bar .rb-sep { color: var(--border); margin: 0 1px; } .ready-bar .rb-link { color: var(--accent); cursor: pointer; } .ready-bar .rb-link:hover { text-decoration: underline; } @@ -826,3 +827,113 @@ } .lesson-candidate-box .lc-rec { border-color: var(--warning); color: var(--warning); } .lesson-candidate-box button:hover { border-color: var(--border-bright); } + + /* ════════════════════════════════════════════════════════════════ + Compact header: top-bar dropdowns + Context Bar + Records line + (collapse the old "select bomb" into role-grouped popovers) + ════════════════════════════════════════════════════════════════ */ + + /* compact toggle chips kept visible in the top bar (Trace / Web) */ + .toggle-chip { font-size: 10.5px; padding: 0 8px; } + + /* a trigger + popover menu (Tools ▾ / Edit ▾ / Records ▾) */ + .hdr-dropdown { position: relative; display: inline-flex; } + .hdr-menu { + position: absolute; + top: calc(100% + 5px); + right: 0; + display: none; + flex-direction: column; + gap: 2px; + min-width: 190px; + max-width: calc(100vw - 16px); + padding: 5px; + background: var(--surface); + border: 1px solid var(--border-bright); + border-radius: 9px; + box-shadow: 0 10px 30px rgba(0,0,0,0.35); + z-index: 300; + } + .hdr-menu.open { display: flex; } + .hdr-menu-wide { width: min(330px, calc(100vw - 16px)); } + .hdr-menu-label { + font-size: 9.5px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: .04em; + color: var(--text-dim); + padding: 5px 8px 2px; + } + .hdr-menu-label:first-child { padding-top: 2px; } + .hdr-menu-item { + display: block; + width: 100%; + text-align: left; + padding: 6px 9px; + background: transparent; + border: none; + border-radius: 6px; + color: var(--text-primary); + font-size: 12px; + cursor: pointer; + white-space: nowrap; + } + .hdr-menu-item:hover { background: var(--control-bg-hover); color: var(--text-bright); } + .hdr-menu .toggle-item::after { content: ' · 꺼짐'; color: var(--text-dim); font-size: 10px; } + .hdr-menu .toggle-item.active { color: var(--accent); } + .hdr-menu .toggle-item.active::after { content: ' · 켜짐'; color: var(--accent); } + .hdr-menu .control-row { margin: 0 4px 2px; } + .hdr-menu .select-wrap { margin: 0 4px 2px; } + + /* Context Bar — "what knowledge context this answer uses" */ + .context-bar { + display: flex; + align-items: center; + gap: 8px; + padding: 5px 12px; + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); + font-size: 11px; + min-width: 0; + } + .context-summary { + flex: 1; + min-width: 0; + display: flex; + align-items: center; + gap: 5px; + overflow-x: auto; + scrollbar-width: none; + white-space: nowrap; + color: var(--text-dim); + } + .context-summary::-webkit-scrollbar { display: none; } + .context-summary .cb-key { color: var(--text-dim); } + .context-summary .cb-val { color: var(--text-bright); font-weight: 600; } + .context-summary .cb-sep { color: var(--border); } + .context-edit-dd { flex-shrink: 0; } + + /* Records line — auto-records status + collapsed record picker */ + .records-line { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 12px; + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); + font-size: 10.5px; + color: var(--text-dim); + min-width: 0; + } + .records-line .rl-summary { + flex: 1; + min-width: 0; + display: flex; + align-items: center; + gap: 6px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + .records-line .rl-latest { color: var(--border-bright); overflow: hidden; text-overflow: ellipsis; } + .records-line .hdr-dropdown { flex-shrink: 0; } diff --git a/media/sidebar.html b/media/sidebar.html index 09a9b25..6eb726d 100644 --- a/media/sidebar.html +++ b/media/sidebar.html @@ -13,70 +13,99 @@
Astra
- - - - - + + +
+ +
+
Tools
+ + + +
+
+
-
-
-
-
Engine
-
-
-
-
-
-
- - - - -
-
-
-
-
- - - -
-
-
-
-
- - -
+
+ +
+ + 준비 상태 확인 중… + +
+ +
+
+ Brain + · + Agent + · + Project +
+
+ +
+
Model
+
+ +
Brain
+
+
+
+ + +
+ +
Agent
+
+
+
+ + + +
+
+ +
Agent ↔ Knowledge
+
+
+
+ + +
+
+ +
Project (Chronicle)
- -
-
-
-
- Auto Records -
-
-
- -
- -
- - 준비 상태 확인 중… + +
+
+ + Auto Records + +
+
+ +
+
Chronicle Records
+
+ + + +
+
@@ -161,10 +190,10 @@
Agent Persona/Instructions
- +
Negative Prompt (Strict Rules)
- +
diff --git a/media/sidebar.js b/media/sidebar.js index ae12c30..88a548b 100644 --- a/media/sidebar.js +++ b/media/sidebar.js @@ -173,62 +173,66 @@ const rbDot = document.getElementById('rbDot'); const rbContent = document.getElementById('rbContent'); const ctxBadge = document.getElementById('ctxBadge'); + const ctxBrainName = document.getElementById('ctxBrainName'); + const ctxAgentName = document.getElementById('ctxAgentName'); + const ctxProjectName = document.getElementById('ctxProjectName'); + const recordsLatest = document.getElementById('recordsLatest'); - // ── Ready-status bar ───────────────────────────────────────────────── - let readyState = {}; + function escAttr(t) { return String(t == null ? '' : t).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); } function fmtK(n) { if (typeof n !== 'number' || !isFinite(n)) return '?'; if (n >= 1000) return (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\.0$/, '') + 'k'; return String(n); } + function shortModel(m) { m = String(m || ''); const i = m.lastIndexOf('/'); return i >= 0 ? m.slice(i + 1) : m; } + function selText(sel) { try { return sel && sel.selectedIndex >= 0 ? (sel.options[sel.selectedIndex].text || '').trim() : ''; } catch { return ''; } } + function truncMid(s, n) { s = String(s || ''); if (s.length <= n) return s; const h = Math.max(4, Math.floor((n - 1) / 2)); return s.slice(0, h) + '…' + s.slice(-h); } + + // ── Context Bar (Brain / Agent / Project summary) + Records line ────── + function syncContextBar() { + if (ctxBrainName) ctxBrainName.textContent = selText(brainSel) || '—'; + if (ctxAgentName) { const t = selText(agentSel); ctxAgentName.textContent = (!t || /no agent/i.test(t)) ? '기본' : t; } + if (ctxProjectName) ctxProjectName.textContent = selText(designerSel) || '—'; + } + function syncRecordsLine() { + if (!recordsLatest) return; + const opt = chronicleRecordSel && chronicleRecordSel.value ? selText(chronicleRecordSel) : ''; + recordsLatest.textContent = opt ? '· ' + truncMid(opt, 38) : ''; + } + + // ── Ready-status bar (Engine / Model / Brain count / Context / Memory) ── + let readyState = {}; function renderReadyBar() { if (!readyBar || !rbContent) return; const s = readyState; const segs = []; - // Engine if (s.engine) { const on = s.engine.online; - const tag = on === true ? '온라인' : on === false ? '오프라인' : '확인 중'; - segs.push(`${s.engine.label || 'Engine'}: ${tag}`); + const tag = on === true ? 'Online' : on === false ? 'Offline' : '확인 중'; + segs.push(`${escAttr(tag)}`); } - // Model if (s.model && s.model.name) { const loaded = s.model.loaded; - const dot = loaded === true ? '● ' : loaded === false ? '○ ' : ''; - segs.push(`${dot}${escAttr(s.model.name)}`); + segs.push(`${escAttr(shortModel(s.model.name))}`); } - // Brain - if (s.brain) { - segs.push(`Brain ${typeof s.brain.files === 'number' ? s.brain.files : '?'} ${escAttr(s.brain.name || '')}`); + if (s.brain && typeof s.brain.files === 'number') { + segs.push(`Brain ${s.brain.files}`); } - // Agent + scope - if (s.agent && s.agent.name) { - const scope = s.agent.scopeFolders > 0 - ? ` (범위 ${s.agent.scopeFolders})` - : ` (범위 미설정)`; - segs.push(`Agent: ${escAttr(s.agent.name)}${scope}`); - } else { - segs.push(`Agent 없음`); - } - // Memory - segs.push(`메모리 ${s.memory ? '켜짐' : '꺼짐'}`); - // Multi-agent (only when on) - if (s.multiAgent) segs.push(`멀티에이전트`); - // Context window (capped for small models gets a ↓ marker) if (typeof s.contextLength === 'number') { if (s.cappedForSmallModel) { - segs.push(`ctx ${fmtK(s.contextLength)} ↓작은모델`); + segs.push(`ctx ${fmtK(s.contextLength)} · 소형모델 제한`); } else { segs.push(`ctx ${fmtK(s.contextLength)}`); } } + segs.push(`메모리 ${s.memory ? 'On' : 'Off'}`); + if (s.multiAgent) segs.push(`멀티에이전트`); rbContent.innerHTML = segs.join('·'); if (rbDot) { const on = s.engine && s.engine.online; rbDot.className = 'rb-dot ' + (on === true ? 'ok' : on === false ? 'bad' : 'warn'); } } - function escAttr(t) { return String(t == null ? '' : t).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); } // ── Context-budget badge (직전 요청 기준) ──────────────────────────── function renderCtxBadge(b) { @@ -587,6 +591,7 @@ const addOpt = document.createElement('option'); addOpt.value = 'new'; addOpt.innerText = '+ Add New Brain...'; brainSel.appendChild(addOpt); + syncContextBar(); break; case 'sessionList': historyList.innerHTML = ''; @@ -653,6 +658,7 @@ vscode.postMessage({ type: 'getAgentContent', path: msg.selected }); } vscode.postMessage({ type: 'getKnowledgeScope', agentPath: msg.selected }); + syncContextBar(); break; case 'agentMapData': if (msg.value) { @@ -728,6 +734,7 @@ newDesignerOpt.value = 'new'; newDesignerOpt.innerText = '+ Add Designer Project...'; designerSel.appendChild(newDesignerOpt); + syncContextBar(); vscode.postMessage({ type: 'getChronicleRecords' }); break; case 'chronicleRecords': @@ -737,6 +744,7 @@ emptyRecordOpt.value = ''; emptyRecordOpt.innerText = 'No records yet'; chronicleRecordSel.appendChild(emptyRecordOpt); + syncRecordsLine(); break; } msg.value.forEach(record => { @@ -746,6 +754,7 @@ o.title = record.path; chronicleRecordSel.appendChild(o); }); + syncRecordsLine(); break; case 'agentContent': agentPrompt.value = msg.value; @@ -1072,6 +1081,38 @@ vscode.postMessage({ type: 'openChronicleRecord', path: chronicleRecordSel.value }); }; + // ── Header dropdowns (Tools ▾ / Edit ▾ / Records ▾) ────────────────── + function closeAllDropdowns(except) { + document.querySelectorAll('.hdr-menu.open').forEach(m => { if (m !== except) m.classList.remove('open'); }); + } + document.querySelectorAll('[data-dd]').forEach(dd => { + const trigger = dd.querySelector('[data-dd-trigger]'); + const menu = dd.querySelector('[data-dd-menu]'); + if (!trigger || !menu) return; + trigger.addEventListener('click', e => { + e.stopPropagation(); + const willOpen = !menu.classList.contains('open'); + closeAllDropdowns(menu); + menu.classList.toggle('open', willOpen); + }); + // Clicks inside the menu shouldn't bubble to the document (which would close it). A click + // on a