v2.2.292-296: 메모리·발열 최적화 + Pixel Office 완전 제거 + 소형 모델 추론 강화(calculate/run_code/지식 스코프)

- v2.2.292 메모리·발열: 헬스체크 워크스페이스 쓰기→fs.access(워처 연쇄 제거)·git 검사 비동기 30분 주기,
  웹뷰 retainContextWhenHidden 정리(채팅만 유지), 브레인 인덱스 유휴 30분 TTL 해제,
  채팅 DOM 200개 상한, @lmstudio/sdk 지연 로드
- v2.2.293 Pixel Office 시각화 폐기: astraOffice 모듈·사이드바 매니저 3종·스프라이트 17MB 삭제,
  sidebarProvider collector 섹션 통삭제 (기업 모드 판단 로직 무변경, vsix 12.2MB→1.3MB)
- v2.2.294 도구 메뉴 'NotebookLM 백엔드 실행/종료' 버튼: OS 자동 감지, 프로젝트 자동 탐색+폴더 선택 저장,
  포트 정리 폴백 (터미널 없이 Research 백엔드 켜고 끄기)
- v2.2.295 추론 강화 1탄: [ACTION 16] <calculate> Python 계산 위임(결과 재주입·자가수정 루프),
  단계별 지식 스코프(General+Specialty, 결정적 도메인 분류, 설정 지식·기억 탭 UI)
- v2.2.296 추론 강화 2탄: [ACTION 17] <run_code> 실행 확인(stdout 회수→수정→재실행 루프),
  문법 오류 재주입 자가수정, 도구 라우팅 힌트(dynamicBlocks), Grounding rescue 도메인 스코프 확장
- 검증: jest 818 통과(신규 29개), tsc 무오류, esbuild 정상

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 18:59:47 +09:00
parent fa397d7fa1
commit bb7cd6b879
466 changed files with 1285 additions and 6588 deletions
+33
View File
@@ -180,6 +180,39 @@
</div>
</section>
<section class="section" data-section="domainKnowledge" data-tab="knowledge">
<h2>단계별 지식 폴더 (소형 모델 정밀 검색)</h2>
<p class="hint">질문이 <strong>수학·논리 / 코딩·디버깅 / 일반 상식</strong>으로 분류되면 전체 두뇌 대신 <strong>General + 해당 Specialty 폴더</strong>만 검색해 무관한 문서를 배제합니다. 경로는 활성 두뇌 기준 상대 경로이며, Specialty 가 비어 있는 단계는 기존처럼 전체 두뇌를 검색합니다.</p>
<div class="row">
<label for="dkGeneral">General Knowledge 폴더 (모든 단계 공통)</label>
<div class="input-group">
<input id="dkGeneral" type="text" placeholder="예: General" spellcheck="false" />
<button data-save="domainKnowledge.general">저장</button>
</div>
</div>
<div class="row">
<label for="dkMath">수학·논리 Specialty 폴더</label>
<div class="input-group">
<input id="dkMath" type="text" placeholder="예: Specialty_Math" spellcheck="false" />
<button data-save="domainKnowledge.math">저장</button>
</div>
</div>
<div class="row">
<label for="dkCoding">코딩·디버깅 Specialty 폴더</label>
<div class="input-group">
<input id="dkCoding" type="text" placeholder="예: 10_Wiki/Topic_Programming" spellcheck="false" />
<button data-save="domainKnowledge.coding">저장</button>
</div>
</div>
<div class="row">
<label for="dkFacts">일반 상식·팩트 Specialty 폴더</label>
<div class="input-group">
<input id="dkFacts" type="text" placeholder="예: Specialty_Facts" spellcheck="false" />
<button data-save="domainKnowledge.facts">저장</button>
</div>
</div>
</section>
<!-- Messenger — 텔레그램 발송 스케줄·알림 (v2.2.284 중앙화) -->
<section class="section" data-section="messenger" data-tab="messenger">
<h2>발송 스케줄</h2>
+23
View File
@@ -30,6 +30,10 @@
const dcBridgeNasUrl = $('dcBridgeNasUrl');
const dcBridgeNasToken = $('dcBridgeNasToken');
const dcSavePath = $('dcSavePath');
const dkGeneral = $('dkGeneral');
const dkMath = $('dkMath');
const dkCoding = $('dkCoding');
const dkFacts = $('dkFacts');
const emailWikiSavePath = $('emailWikiSavePath');
const emailOutlookEnabled = $('emailOutlookEnabled');
const emailGmailEnabled = $('emailGmailEnabled');
@@ -152,6 +156,18 @@
document.querySelector('[data-save="datacollect.savePath"]').addEventListener('click', () =>
vscode.postMessage({ type: 'datacollect.update', savePath: dcSavePath.value })
);
document.querySelector('[data-save="domainKnowledge.general"]').addEventListener('click', () =>
vscode.postMessage({ type: 'domainKnowledge.update', general: dkGeneral.value })
);
document.querySelector('[data-save="domainKnowledge.math"]').addEventListener('click', () =>
vscode.postMessage({ type: 'domainKnowledge.update', math: dkMath.value })
);
document.querySelector('[data-save="domainKnowledge.coding"]').addEventListener('click', () =>
vscode.postMessage({ type: 'domainKnowledge.update', coding: dkCoding.value })
);
document.querySelector('[data-save="domainKnowledge.facts"]').addEventListener('click', () =>
vscode.postMessage({ type: 'domainKnowledge.update', facts: dkFacts.value })
);
document.querySelector('[data-save="email.wikifySavePath"]').addEventListener('click', () =>
vscode.postMessage({ type: 'datacollect.update', emailWikifySavePath: emailWikiSavePath.value })
);
@@ -431,6 +447,13 @@
: `사이드바에서 선택한 모델이 여기에도 동기화됩니다. (${list.length}개 발견)`;
// ---- Datacollect ----
const dk = state.domainKnowledge;
if (dk) {
setIfNotFocused(dkGeneral, dk.general);
setIfNotFocused(dkMath, dk.math);
setIfNotFocused(dkCoding, dk.coding);
setIfNotFocused(dkFacts, dk.facts);
}
const dc = state.datacollect;
if (dc) {
if (dcBridgeTarget && document.activeElement !== dcBridgeTarget && (dc.bridgeTarget === 'local' || dc.bridgeTarget === 'nas')) {
+3 -1
View File
@@ -54,6 +54,8 @@
<button class="hdr-menu-item toggle-item" id="brainTraceDebugBtn" data-tooltip="근거 추적의 원본 JSON 표시 (개발자용)">근거 추적 JSON 보기</button>
<button class="hdr-menu-item" id="saveWikiRawBtn" data-tooltip="현재 답변의 원본 마크다운을 두뇌(지식)에 저장">원본 답변을 두뇌에 저장</button>
<button class="hdr-menu-item" id="brainBtn" data-tooltip="두뇌(지식) 폴더 변경사항을 git commit + push">두뇌 동기화</button>
<button class="hdr-menu-item" id="nlmBridgeStartBtn" data-tooltip="Datacollector Research(NotebookLM)용 로컬 백엔드를 실행합니다 — 터미널 조작 불필요">NotebookLM 백엔드 실행</button>
<button class="hdr-menu-item" id="nlmBridgeStopBtn" data-tooltip="로컬 백엔드 종료 (NAS 브리지·Astra 기능에는 영향 없음)">NotebookLM 백엔드 종료</button>
</div>
</div>
<button class="icon-btn" id="historyBtn" data-tooltip="이전 대화 기록 보기">기록</button>
@@ -404,7 +406,7 @@
<!--
Pixel Office 채팅 사이드바 배너는 v2.2.267 에서 제거됨 (화면 점유 문제).
백엔드 파이프라인 상태·풀스크린 Astra Office 패널(g1nation.company.pixelOffice.open)은 유지.
백엔드 파이프라인 상태는 유지 (Pixel Office 시각화는 v2.2.293 에서 완전 제거).
-->
<div class="chat" id="chat">
+24 -6
View File
@@ -812,9 +812,28 @@
msgEl.appendChild(head); msgEl.appendChild(body);
msgEl.appendChild(actions);
chat.appendChild(msgEl); chat.scrollTop = chat.scrollHeight;
trimChatDom();
return { body, msgEl };
}
// [메모리 개선] 채팅 DOM 상한 — 이 웹뷰는 retainContextWhenHidden 이라 하루 종일 켜두면
// 메시지 노드가 무한히 쌓여 메모리가 단조 증가한다. 최근 MAX_CHAT_DOM_MESSAGES 개만
// DOM 에 유지하고 오래된 것부터 제거한다 (내용이 필요한 메시지는 Copy/Export 로 보존).
const MAX_CHAT_DOM_MESSAGES = 200;
let _trimNotice = null;
function trimChatDom() {
const msgs = chat.querySelectorAll(':scope > .msg');
const excess = msgs.length - MAX_CHAT_DOM_MESSAGES;
if (excess <= 0) return;
for (let i = 0; i < excess; i++) msgs[i].remove();
if (!_trimNotice || !_trimNotice.isConnected) {
_trimNotice = document.createElement('div');
_trimNotice.style.cssText = 'text-align:center;opacity:.55;font-size:11px;padding:6px 0;';
_trimNotice.textContent = '⏳ 오래된 메시지는 메모리 절약을 위해 화면에서 정리되었습니다';
}
chat.insertBefore(_trimNotice, chat.firstChild);
}
window.addEventListener('message', e => {
const msg = e.data;
switch(msg.type) {
@@ -1119,12 +1138,6 @@
chatEl.scrollTop = chatEl.scrollHeight;
break;
}
case 'pixelOfficeUpdate':
case 'officeSnapshot': {
// Pixel Office 채팅 배너는 v2.2.267 에서 제거 — 백엔드가 여전히 보내는
// 상태 메시지는 조용히 무시 (풀스크린 Astra Office 패널은 별도 webview 로 수신).
break;
}
case 'devilRebuttal': {
// Devil Agent (도현) 카드. main assistant 답변 직후 chat 하단에 한 장 추가.
// 사용자가 '재반박' 누르면 그 텍스트가 다음 user prompt 로 가서 main turn 한 번 더 돌고,
@@ -1928,6 +1941,11 @@
const syncBrain = () => { Sound.play(550, 'sine', 0.1); vscode.postMessage({ type: 'syncBrain' }); };
document.getElementById('brainBtn').onclick = syncBrain;
saveWikiRawBtn.onclick = () => vscode.postMessage({ type: 'saveWikiRaw' });
// NotebookLM 로컬 백엔드 (Datacollector Research 용) 실행/종료 — 터미널 없이 버튼으로.
const nlmBridgeStartBtn = document.getElementById('nlmBridgeStartBtn');
if (nlmBridgeStartBtn) nlmBridgeStartBtn.onclick = () => vscode.postMessage({ type: 'startNotebookLmBridge' });
const nlmBridgeStopBtn = document.getElementById('nlmBridgeStopBtn');
if (nlmBridgeStopBtn) nlmBridgeStopBtn.onclick = () => vscode.postMessage({ type: 'stopNotebookLmBridge' });
addBrainBtn.onclick = () => vscode.postMessage({ type: 'addBrain' });
// v2.2.67 — 만약 dropdown 이 'new' 상태로 굳어있어도 수정/삭제는 직전 유효 선택(또는 첫 실제 옵션)
// 을 기준으로 동작하도록 폴백. 이전엔 'new' 인 순간 그냥 early-return 해서 "수정 안 됨" 버그 발생.