chore: version up to 2.80.34 and package

This commit is contained in:
g1nation
2026-05-12 22:54:21 +09:00
parent 148bfb070b
commit 065e598cca
26 changed files with 2023 additions and 139 deletions
+66
View File
@@ -726,3 +726,69 @@
from { opacity: 0; transform: translateX(-10px); }
to { opacity: 1; transform: translateX(0); }
}
/* ── Ready-status bar ───────────────────────────────────────────── */
.ready-bar {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
font-size: 10.5px;
line-height: 1.4;
color: var(--text-dim);
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
white-space: nowrap;
overflow-x: auto;
scrollbar-width: none;
}
.ready-bar::-webkit-scrollbar { display: none; }
.ready-bar .rb-dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--text-dim); flex-shrink: 0;
}
.ready-bar .rb-dot.ok { background: var(--success); }
.ready-bar .rb-dot.bad { background: var(--error); }
.ready-bar .rb-dot.warn { background: var(--warning); }
.ready-bar .rb-content { display: flex; align-items: center; gap: 6px; flex-wrap: nowrap; }
.ready-bar .rb-seg { color: var(--text-dim); }
.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-sep { color: var(--border); margin: 0 1px; }
.ready-bar .rb-link { color: var(--accent); cursor: pointer; }
.ready-bar .rb-link:hover { text-decoration: underline; }
/* ── Context-budget badge (input footer) ────────────────────────── */
.ctx-badge {
font-size: 10px;
color: var(--text-dim);
padding: 1px 6px;
border-radius: 9px;
border: 1px solid var(--border);
background: transparent;
white-space: nowrap;
max-width: 60vw;
overflow: hidden;
text-overflow: ellipsis;
}
.ctx-badge:empty { display: none; }
.ctx-badge.ok { border-color: var(--border); }
.ctx-badge.warn { color: var(--warning); border-color: var(--warning); }
/* ── Per-answer "scope used" footer ─────────────────────────────── */
.msg-scope-footer {
margin-top: 6px;
padding-top: 6px;
border-top: 1px dashed var(--border);
font-size: 10px;
color: var(--text-dim);
line-height: 1.5;
word-break: break-word;
}
.msg-scope-footer .scope-link {
color: var(--accent);
cursor: pointer;
}
.msg-scope-footer .scope-link:hover { text-decoration: underline; }
.msg-scope-footer .scope-dim { color: var(--border-bright); }
+6
View File
@@ -74,6 +74,11 @@
</div>
</div>
<div id="readyBar" class="ready-bar" title="현재 준비 상태 — 엔진 / 모델 / Brain / Agent 범위 / 메모리 / 컨텍스트 창">
<span class="rb-dot" id="rbDot"></span>
<span class="rb-content" id="rbContent">준비 상태 확인 중…</span>
</div>
<div id="historyOverlay" class="history-overlay">
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:20px;">
<h2 style="color:var(--text-bright);">Chat History</h2>
@@ -169,6 +174,7 @@
<div class="footer-left">
<button class="icon-btn" id="attachBtn" title="Attach Files">📎</button>
<span id="statusLabel" style="font-size:10px; color:var(--text-dim);">Ready</span>
<span id="ctxBadge" class="ctx-badge" title="직전 요청에 실제로 들어간 컨텍스트 추정치"></span>
</div>
<div class="footer-right">
<button id="cancelBtn" class="cancel-btn" title="Clear draft" style="display:none;">✕ Clear</button>
+153
View File
@@ -169,6 +169,140 @@
const skillFolderList = document.getElementById('skillFolderList');
const agentMapAgentName = document.getElementById('agentMapAgentName');
const agentMapStatus = document.getElementById('agentMapStatus');
const readyBar = document.getElementById('readyBar');
const rbDot = document.getElementById('rbDot');
const rbContent = document.getElementById('rbContent');
const ctxBadge = document.getElementById('ctxBadge');
// ── Ready-status bar ─────────────────────────────────────────────────
let readyState = {};
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 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(`<span class="rb-seg ${on === false ? 'bad' : on === true ? 'ok' : ''}">${s.engine.label || 'Engine'}: ${tag}</span>`);
}
// Model
if (s.model && s.model.name) {
const loaded = s.model.loaded;
const dot = loaded === true ? '● ' : loaded === false ? '○ ' : '';
segs.push(`<span class="rb-seg" title="${loaded === true ? '메모리에 로드됨' : loaded === false ? '아직 로드되지 않음' : ''}">${dot}${escAttr(s.model.name)}</span>`);
}
// Brain
if (s.brain) {
segs.push(`<span class="rb-seg">Brain ${typeof s.brain.files === 'number' ? s.brain.files : '?'}<span class="rb-dim"> ${escAttr(s.brain.name || '')}</span></span>`);
}
// Agent + scope
if (s.agent && s.agent.name) {
const scope = s.agent.scopeFolders > 0
? ` <span class="rb-link" data-act="map">(범위 ${s.agent.scopeFolders})</span>`
: ` <span class="rb-dim">(범위 미설정)</span>`;
segs.push(`<span class="rb-seg">Agent: ${escAttr(s.agent.name)}${scope}</span>`);
} else {
segs.push(`<span class="rb-seg rb-dim">Agent 없음</span>`);
}
// Memory
segs.push(`<span class="rb-seg ${s.memory ? '' : 'rb-dim'}">메모리 ${s.memory ? '켜짐' : '꺼짐'}</span>`);
// Multi-agent (only when on)
if (s.multiAgent) segs.push(`<span class="rb-seg">멀티에이전트</span>`);
// Context window (capped for small models gets a ↓ marker)
if (typeof s.contextLength === 'number') {
if (s.cappedForSmallModel) {
segs.push(`<span class="rb-seg" title="작은 모델(≤4B) 감지 — 예산을 ${fmtK(s.contextLength)} tokens 로 축소 (설정 g1nation.contextLength = ${fmtK(s.nominalContextLength)}). g1nation.smallModelContextCap 로 조절.">ctx ${fmtK(s.contextLength)}<span class="rb-dim"> ↓작은모델</span></span>`);
} else {
segs.push(`<span class="rb-seg" title="모델 context window (g1nation.contextLength). 실제 로드된 값과 맞춰주세요.">ctx ${fmtK(s.contextLength)}</span>`);
}
}
rbContent.innerHTML = segs.join('<span class="rb-sep">·</span>');
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 => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c])); }
// ── Context-budget badge (직전 요청 기준) ────────────────────────────
function renderCtxBadge(b) {
if (!ctxBadge) return;
if (!b || typeof b.inputTokens !== 'number') { ctxBadge.textContent = ''; ctxBadge.className = 'ctx-badge'; ctxBadge.title = ''; return; }
const parts = [`${fmtK(b.inputTokens)} in / ${fmtK(b.maxOutputTokens)} out`];
if (typeof b.contextLength === 'number') {
parts.push(b.cappedForSmallModel ? `ctx ${fmtK(b.contextLength)}` : `ctx ${fmtK(b.contextLength)}`);
}
if (typeof b.brainFiles === 'number' && b.brainFiles > 0) parts.push(`Brain ${b.brainFiles}`);
if (b.includesOpenFile) parts.push('📄 열린 파일');
if (b.imageCount > 0) parts.push(`🖼 ${b.imageCount}`);
if (b.droppedHistory > 0) parts.push(`기록 ${b.droppedHistory}`);
if (b.systemTruncated) parts.push('컨텍스트 일부 생략');
if (b.cappedForSmallModel) parts.push('🔻 작은 모델 모드');
if (b.tight) parts.push('⚠ 컨텍스트 거의 가득');
const warn = b.tight || b.systemTruncated;
ctxBadge.textContent = parts.join(' · ');
ctxBadge.className = 'ctx-badge' + (warn ? ' warn' : ' ok');
ctxBadge.title = `model: ${b.model || ''}${b.paramB != null ? ' (~' + b.paramB + 'B)' : ''}\n입력 ≈ ${b.inputTokens} tokens (시스템 ${b.systemTokens}, 기록 ${b.historyKept}개)\n출력 상한 ${b.maxOutputTokens} tokens / 유효 context window ${b.contextLength} tokens${b.cappedForSmallModel ? ' (작은 모델용 축소; 설정값 ' + b.nominalContextLength + ')' : ''}`;
}
if (readyBar) {
readyBar.addEventListener('click', e => {
const t = e.target;
if (t && t.dataset && t.dataset.act === 'map') vscode.postMessage({ type: 'editKnowledgeMap' });
});
}
// ── Per-answer "scope used" footer ──────────────────────────────────
const MEMORY_LAYER_LABELS = {
'long-term-memory': '장기기억',
'project-memory': '프로젝트기억',
'procedural-memory': '절차기억',
'episodic-memory': '에피소드기억',
'project-scan': '프로젝트스캔',
'recent-knowledge': '최근지식',
};
function dirOf(rel) {
const i = Math.max(rel.lastIndexOf('/'), rel.lastIndexOf('\\'));
return i > 0 ? rel.slice(0, i) : '(루트)';
}
function renderScopeFooter(target, v) {
if (!target) return;
const old = target.querySelector('.msg-scope-footer');
if (old) old.remove();
const footer = document.createElement('div');
footer.className = 'msg-scope-footer';
const files = Array.isArray(v.usedBrainFiles) ? v.usedBrainFiles : [];
const layers = (Array.isArray(v.usedMemoryLayers) ? v.usedMemoryLayers : []).map(s => MEMORY_LAYER_LABELS[s] || s);
if (files.length === 0 && layers.length === 0) {
footer.innerHTML = `<span class="scope-link" data-act="map" title="에이전트↔지식 매핑 편집">🔎 참조 지식 없음</span> <span class="scope-dim">— 모델 자체 지식으로 답변</span>`;
} else {
const dirs = Array.from(new Set(files.map(dirOf)));
let scopeLabel;
if (v.scoped && Array.isArray(v.configuredFolders) && v.configuredFolders.length) {
scopeLabel = v.configuredFolders.join(', ');
} else if (dirs.length) {
scopeLabel = dirs.slice(0, 4).join(', ') + (dirs.length > 4 ? `${dirs.length - 4}` : '');
} else {
scopeLabel = '전체 브레인';
}
const agentTag = v.agentName ? `[${escAttr(v.agentName)}] ` : '';
const fileTag = files.length ? ` <span class="scope-dim">· 파일 ${files.length}</span>` : '';
const layerTag = layers.length ? ` <span class="scope-dim">· 메모리 ${escAttr(layers.join('·'))}</span>` : '';
const titleAttr = files.length ? `사용된 브레인 파일:\n${files.join('\n')}` : '에이전트↔지식 매핑 편집';
footer.innerHTML = `<span class="scope-link" data-act="map" title="${escAttr(titleAttr)}">🔎 참조: ${agentTag}${escAttr(scopeLabel)}</span>${fileTag}${layerTag}`;
}
footer.addEventListener('click', e => {
const t = e.target;
if (t && t.dataset && t.dataset.act === 'map') vscode.postMessage({ type: 'editKnowledgeMap' });
});
const actions = target.querySelector('.msg-actions');
if (actions) target.insertBefore(footer, actions); else target.appendChild(footer);
}
let agentMapDraft = { agentPath: '', name: '', knowledgeFolders: [], skillFolders: [] };
@@ -348,6 +482,7 @@
setGenerating(false);
resetStepper();
Sound.success();
vscode.postMessage({ type: 'getReadyStatus' });
break;
case 'restoreHistory':
case 'sessionLoaded':
@@ -433,7 +568,25 @@
case 'engineStatus':
statusDot.style.background = msg.value.online ? 'var(--success)' : 'var(--error)';
engineStatusText.innerText = msg.value.online ? 'Online' : 'Offline';
readyState.engine = Object.assign({}, readyState.engine, { online: !!msg.value.online });
renderReadyBar();
break;
case 'readyStatus':
readyState = Object.assign({}, readyState, msg.value || {});
renderReadyBar();
break;
case 'contextBudget':
renderCtxBadge(msg.value);
break;
case 'usedScope': {
let target = streamBody && streamBody._parent;
if (!target) {
const all = chat.querySelectorAll('.msg.msg-ai');
target = all[all.length - 1];
}
renderScopeFooter(target, msg.value || {});
break;
}
case 'autoContinue':
statusLabel.innerText = msg.value; thinkingBar.classList.add('active');
if (msg.value.includes('Analyzing')) setStep('analyze');