feat: Bridge 타깃 토글 + /research 제거 + 환각·오염 방지 강화 (v2.2.205)

- Datacollect Bridge 로컬/NAS 타깃 토글(Settings 패널) + NAS URL/x-bridge-token.
  기본 local = 현행 동작 유지. (백엔드 NAS 분리 준비)
- /research(NotebookLM) 제거 — 로컬 Datacollect 앱 전용으로 분리.
- 에러로그 오염 차단: STT/스택트레이스/에러덤프를 장기기억 채굴 제외 + 자동
  추출 항목 14일 TTL(참조 시 슬라이딩 연장). 기존·수동 항목 무영향.
- 컨텍스트 [주제] 태깅 + 교차오염 방지 경계 지침.
- "확인 불가" 사실 날조 금지 규칙(R7과 구분).
- /meet STT 오타 보정: 철자 정규화 허용하되 사실 날조는 차단.

타입체크 + 407 테스트 통과.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-05 16:47:55 +09:00
parent 2ea5185cd6
commit 6b017b0d31
14 changed files with 213 additions and 127 deletions
+29 -2
View File
@@ -48,14 +48,41 @@
<!-- Datacollect -->
<section class="section" data-section="datacollect">
<h2>Datacollect (slash 명령)</h2>
<p class="hint">채팅에서 <code>/research</code> · <code>/benchmark</code> · <code>/youtube</code> 를 입력하면 Datacollect Bridge로 위임됩니다. Bridge는 Datacollect 프로젝트에서 <code>npm run bridge</code> 로 실행해야 합니다.</p>
<p class="hint">채팅에서 <code>/research</code> · <code>/benchmark</code> · <code>/youtube</code> 를 입력하면 Datacollect Bridge로 위임됩니다. <strong>타깃</strong>으로 로컬(<code>npm run bridge</code>) 또는 NAS의 경량 Bridge 중 어디를 호출할지 선택합니다.</p>
<div class="row">
<label for="dcBridgeUrl">Bridge URL</label>
<label for="dcBridgeTarget">Bridge 타깃</label>
<div class="input-group narrow">
<select id="dcBridgeTarget">
<option value="local">로컬 (Local)</option>
<option value="nas">NAS</option>
</select>
<button data-save="datacollect.bridgeTarget">저장</button>
</div>
<small class="hint"><strong>local</strong> = 아래 로컬 Bridge URL 사용. <strong>nas</strong> = NAS Bridge URL(+토큰) 사용. nas인데 URL이 비어 있으면 안전하게 로컬로 폴백합니다.</small>
</div>
<div class="row">
<label for="dcBridgeUrl">로컬 Bridge URL</label>
<div class="input-group">
<input id="dcBridgeUrl" type="text" placeholder="http://127.0.0.1:3002" spellcheck="false" />
<button data-save="datacollect.bridgeUrl">저장</button>
</div>
</div>
<div class="row">
<label for="dcBridgeNasUrl">NAS Bridge URL</label>
<div class="input-group">
<input id="dcBridgeNasUrl" type="text" placeholder="https://your-nas-domain 또는 http://nas-ip:3002" spellcheck="false" />
<button data-save="datacollect.bridgeNasUrl">저장</button>
</div>
<small class="hint">타깃이 <strong>nas</strong>일 때 호출할 NAS 경량 Bridge 주소.</small>
</div>
<div class="row">
<label for="dcBridgeNasToken">NAS Bridge 토큰</label>
<div class="input-group">
<input id="dcBridgeNasToken" type="text" placeholder="(NAS의 BRIDGE_AUTH_TOKEN 값)" spellcheck="false" />
<button data-save="datacollect.bridgeNasToken">저장</button>
</div>
<small class="hint">NAS Bridge의 <code>x-bridge-token</code>. <strong>nas</strong> 타깃일 때만 요청 헤더에 실립니다.</small>
</div>
<div class="row">
<label for="dcSavePath">결과물 저장 폴더</label>
<div class="input-group">
+17
View File
@@ -25,7 +25,10 @@
const cnModelHint = $('cnModelHint');
// ---- Datacollect ----
const dcBridgeTarget = $('dcBridgeTarget');
const dcBridgeUrl = $('dcBridgeUrl');
const dcBridgeNasUrl = $('dcBridgeNasUrl');
const dcBridgeNasToken = $('dcBridgeNasToken');
const dcSavePath = $('dcSavePath');
const dcCrawlDepth = $('dcCrawlDepth');
const dcMaxPages = $('dcMaxPages');
@@ -125,9 +128,18 @@
);
// ---- Datacollect listeners ----
document.querySelector('[data-save="datacollect.bridgeTarget"]').addEventListener('click', () =>
vscode.postMessage({ type: 'datacollect.update', bridgeTarget: dcBridgeTarget.value })
);
document.querySelector('[data-save="datacollect.bridgeUrl"]').addEventListener('click', () =>
vscode.postMessage({ type: 'datacollect.update', bridgeUrl: dcBridgeUrl.value })
);
document.querySelector('[data-save="datacollect.bridgeNasUrl"]').addEventListener('click', () =>
vscode.postMessage({ type: 'datacollect.update', bridgeNasUrl: dcBridgeNasUrl.value })
);
document.querySelector('[data-save="datacollect.bridgeNasToken"]').addEventListener('click', () =>
vscode.postMessage({ type: 'datacollect.update', bridgeNasToken: dcBridgeNasToken.value })
);
document.querySelector('[data-save="datacollect.savePath"]').addEventListener('click', () =>
vscode.postMessage({ type: 'datacollect.update', savePath: dcSavePath.value })
);
@@ -385,7 +397,12 @@
// ---- Datacollect ----
const dc = state.datacollect;
if (dc) {
if (dcBridgeTarget && document.activeElement !== dcBridgeTarget && (dc.bridgeTarget === 'local' || dc.bridgeTarget === 'nas')) {
dcBridgeTarget.value = dc.bridgeTarget;
}
setIfNotFocused(dcBridgeUrl, dc.bridgeUrl);
setIfNotFocused(dcBridgeNasUrl, dc.bridgeNasUrl);
setIfNotFocused(dcBridgeNasToken, dc.bridgeNasToken);
setIfNotFocused(dcSavePath, dc.savePath);
setIfNotFocused(dcCrawlDepth, dc.crawlDepth);
setIfNotFocused(dcMaxPages, dc.maxPages);