Update ConnectAI codebase
This commit is contained in:
@@ -284,6 +284,18 @@
|
||||
<div id="providersError" class="error" hidden></div>
|
||||
</section>
|
||||
|
||||
<!-- Devil Agent (도현) -->
|
||||
<section class="section" data-section="devilAgent">
|
||||
<h2>🎭 Devil's Advocate (도현)</h2>
|
||||
<p class="hint">매 답변 직후 별도 LLM 호출로 *비판적 sparring partner* 가 한 문단 반박. 사용자의 사고를 능동적 방어로 전환. 같은 모델 재사용 (~10-15% 추가 비용).</p>
|
||||
<div class="row toggle">
|
||||
<label><input id="devilEnabled" type="checkbox"> 도현 활성화 — 답변마다 반박 카드</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<small class="hint">규칙: 한 답변당 약점 1개만, 통계·수치 인용 금지 (환각 차단), 끝에 우려+검증 방법 명시. 명령 팔레트 <code>Astra: Toggle Devil Agent 🎭</code> 로도 토글.</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Advanced -->
|
||||
<section class="section" data-section="advanced">
|
||||
<h2>고급</h2>
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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 테두리. */
|
||||
|
||||
@@ -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 = `
|
||||
<div class="devil-rebuttal-head">🎭 <strong>${escAttr(persona)}</strong>이(가) 반박합니다</div>
|
||||
<div class="devil-rebuttal-body"></div>
|
||||
<div class="devil-rebuttal-actions">
|
||||
<button class="devil-reply">재반박</button>
|
||||
<button class="devil-dismiss ghost">넘기기</button>
|
||||
</div>`;
|
||||
// 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 → 자동 진행 안내(읽기 전용)
|
||||
|
||||
Reference in New Issue
Block a user