release: v2.0.3 - AI 1-Person Company Engine & Business Intelligence
This commit is contained in:
@@ -777,6 +777,25 @@
|
||||
vscode.postMessage({ type: 'getKnowledgeScope', agentPath: msg.selected });
|
||||
syncContextBar();
|
||||
break;
|
||||
case 'companyStatus': {
|
||||
const v = msg.value || {};
|
||||
renderCompanyChip(!!v.enabled, v.summary || '');
|
||||
break;
|
||||
}
|
||||
case 'companyAgents': {
|
||||
renderCompanyAgentCards(msg.value || {});
|
||||
break;
|
||||
}
|
||||
case 'openCompanyManageOverlay': {
|
||||
// Triggered by the Command Palette `Manage 1인 기업 Agents`.
|
||||
document.getElementById('companyOverlay')?.classList.add('visible');
|
||||
vscode.postMessage({ type: 'getCompanyAgents' });
|
||||
break;
|
||||
}
|
||||
case 'companyTurnUpdate': {
|
||||
if (msg.value) renderCompanyPhase(msg.value);
|
||||
break;
|
||||
}
|
||||
case 'architectureStatus': {
|
||||
// Show / hide the chip + reflect current state.
|
||||
const chip = document.getElementById('archChip');
|
||||
@@ -1350,6 +1369,7 @@
|
||||
vscode.postMessage({ type: 'getChronicleRecords' });
|
||||
vscode.postMessage({ type: 'getKnowledgeMix' });
|
||||
vscode.postMessage({ type: 'getArchitectureStatus' });
|
||||
vscode.postMessage({ type: 'getCompanyStatus' });
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
|
||||
// ── Project Architecture chip buttons ─────────────────────────────────
|
||||
@@ -1360,6 +1380,180 @@
|
||||
if (_archRefreshBtn) _archRefreshBtn.onclick = () => vscode.postMessage({ type: 'refreshArchitecture' });
|
||||
if (_archDetachBtn) _archDetachBtn.onclick = () => vscode.postMessage({ type: 'detachArchitecture' });
|
||||
|
||||
// ── 1인 기업 (Company) Mode chip + manage overlay ─────────────────────
|
||||
// The chip itself toggles enabled/disabled. The ▾ button opens the
|
||||
// manage overlay where the user picks active agents + per-agent
|
||||
// model overrides. State round-trips through `companyStatus` /
|
||||
// `companyAgents` messages so the webview and extension stay in sync.
|
||||
const _companyChip = document.getElementById('companyChip');
|
||||
const _companyChipLabel = document.getElementById('companyChipLabel');
|
||||
const _companyManageBtn = document.getElementById('companyManageBtn');
|
||||
const _companyOverlay = document.getElementById('companyOverlay');
|
||||
const _closeCompanyBtns = [
|
||||
document.getElementById('closeCompanyOverlayBtn'),
|
||||
document.getElementById('closeCompanyOverlayBtn2'),
|
||||
].filter(Boolean);
|
||||
const _companyNameInput = document.getElementById('companyNameInput');
|
||||
const _saveCompanyNameBtn = document.getElementById('saveCompanyNameBtn');
|
||||
const _companyAgentList = document.getElementById('companyAgentList');
|
||||
const _companyStatusEl = document.getElementById('companyStatus');
|
||||
|
||||
const renderCompanyChip = (active, summary) => {
|
||||
if (!_companyChip || !_companyChipLabel) return;
|
||||
_companyChip.setAttribute('data-active', active ? 'true' : 'false');
|
||||
_companyChipLabel.textContent = active ? (summary || 'Company ON') : 'Company OFF';
|
||||
};
|
||||
|
||||
if (_companyChip) {
|
||||
_companyChip.onclick = () => {
|
||||
const isActive = _companyChip.getAttribute('data-active') === 'true';
|
||||
// Optimistic flip — backend echoes the canonical state back.
|
||||
renderCompanyChip(!isActive, _companyChipLabel?.textContent || '');
|
||||
vscode.postMessage({ type: 'setCompanyEnabled', value: !isActive });
|
||||
};
|
||||
}
|
||||
if (_companyManageBtn) {
|
||||
_companyManageBtn.onclick = () => {
|
||||
if (!_companyOverlay) return;
|
||||
_companyOverlay.classList.add('visible');
|
||||
_companyStatusEl.textContent = '불러오는 중...';
|
||||
vscode.postMessage({ type: 'getCompanyAgents' });
|
||||
};
|
||||
}
|
||||
for (const btn of _closeCompanyBtns) {
|
||||
btn.onclick = () => _companyOverlay?.classList.remove('visible');
|
||||
}
|
||||
if (_saveCompanyNameBtn && _companyNameInput) {
|
||||
_saveCompanyNameBtn.onclick = () => {
|
||||
vscode.postMessage({ type: 'setCompanyName', value: _companyNameInput.value });
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the agent cards in the manage overlay. Each card has a
|
||||
* toggle (active on/off) and a model input (per-agent override).
|
||||
* CEO is rendered but locked-on; clicking its toggle is a no-op.
|
||||
*/
|
||||
function renderCompanyAgentCards(payload) {
|
||||
if (!_companyAgentList) return;
|
||||
_companyAgentList.innerHTML = '';
|
||||
if (_companyNameInput && payload && typeof payload.companyName === 'string') {
|
||||
_companyNameInput.value = payload.companyName;
|
||||
}
|
||||
const agents = (payload && Array.isArray(payload.agents)) ? payload.agents : [];
|
||||
for (const a of agents) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'company-agent-card';
|
||||
li.setAttribute('data-active', a.active ? 'true' : 'false');
|
||||
if (a.alwaysOn) li.setAttribute('data-locked', 'true');
|
||||
|
||||
const emoji = document.createElement('span');
|
||||
emoji.className = 'company-agent-emoji';
|
||||
emoji.textContent = a.emoji;
|
||||
|
||||
const body = document.createElement('div');
|
||||
body.className = 'company-agent-body';
|
||||
const name = document.createElement('div');
|
||||
name.className = 'company-agent-name';
|
||||
name.innerHTML = `${escAttr(a.name)} <span class="company-agent-role">${escAttr(a.role)}</span>`;
|
||||
const tag = document.createElement('div');
|
||||
tag.className = 'company-agent-tagline';
|
||||
tag.textContent = a.tagline || '';
|
||||
tag.title = a.specialty || '';
|
||||
body.appendChild(name);
|
||||
body.appendChild(tag);
|
||||
|
||||
const controls = document.createElement('div');
|
||||
controls.className = 'company-agent-controls';
|
||||
|
||||
const modelInput = document.createElement('input');
|
||||
modelInput.type = 'text';
|
||||
modelInput.className = 'company-agent-model';
|
||||
modelInput.placeholder = 'default';
|
||||
modelInput.value = a.modelOverride || '';
|
||||
modelInput.title = '비워두면 글로벌 기본 모델 사용';
|
||||
modelInput.onchange = () => {
|
||||
vscode.postMessage({
|
||||
type: 'setCompanyAgentModel',
|
||||
agentId: a.id,
|
||||
model: modelInput.value.trim(),
|
||||
});
|
||||
};
|
||||
|
||||
const toggle = document.createElement('button');
|
||||
toggle.className = 'company-agent-toggle';
|
||||
toggle.textContent = a.active ? 'ON' : 'OFF';
|
||||
if (a.alwaysOn) {
|
||||
toggle.disabled = true;
|
||||
toggle.textContent = 'LOCKED';
|
||||
} else {
|
||||
toggle.onclick = () => {
|
||||
// Optimistic update + send the full new list so the
|
||||
// backend has a single canonical replace operation.
|
||||
const wantActive = !(li.getAttribute('data-active') === 'true');
|
||||
li.setAttribute('data-active', wantActive ? 'true' : 'false');
|
||||
toggle.textContent = wantActive ? 'ON' : 'OFF';
|
||||
const nextIds = Array.from(_companyAgentList.querySelectorAll('.company-agent-card'))
|
||||
.filter(el => el.getAttribute('data-active') === 'true')
|
||||
.map(el => el.dataset.agentId)
|
||||
.filter(Boolean);
|
||||
vscode.postMessage({ type: 'setCompanyActiveAgents', value: nextIds });
|
||||
};
|
||||
}
|
||||
li.dataset.agentId = a.id;
|
||||
controls.appendChild(modelInput);
|
||||
controls.appendChild(toggle);
|
||||
|
||||
li.appendChild(emoji);
|
||||
li.appendChild(body);
|
||||
li.appendChild(controls);
|
||||
_companyAgentList.appendChild(li);
|
||||
}
|
||||
if (_companyStatusEl) _companyStatusEl.textContent = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render one phase event from the dispatcher. The chat gets a
|
||||
* card per phase so the user can follow progress in real time —
|
||||
* "🧭 CEO 작업 분배 중..." → "📺 레오 작업 수행 중..." → final report.
|
||||
*/
|
||||
function renderCompanyPhase(ev) {
|
||||
const chatEl = document.getElementById('chat');
|
||||
if (!chatEl) return;
|
||||
const card = document.createElement('div');
|
||||
card.className = 'company-phase-card';
|
||||
if (ev.phase === 'plan-start') {
|
||||
card.innerHTML = '<div class="cph-head">🧭 CEO</div><div class="cph-meta">작업 분배 중…</div>';
|
||||
} else if (ev.phase === 'plan-ready') {
|
||||
const tasks = (ev.plan?.tasks || []).map((t, i) => `${i + 1}. <strong>${escAttr(t.agent)}</strong> — ${escAttr(t.task)}`).join('<br>');
|
||||
card.innerHTML = `<div class="cph-head">🧭 CEO 브리프</div>
|
||||
<div>${escAttr(ev.plan?.brief || '(brief 없음)')}</div>
|
||||
<div class="cph-meta" style="margin-top:6px">${tasks || '(no tasks — chat reply)'}</div>`;
|
||||
} else if (ev.phase === 'agent-start') {
|
||||
card.innerHTML = `<div class="cph-head">${escAttr(ev.agentId)} 작업 수행 중…</div>
|
||||
<div class="cph-meta">${escAttr(ev.task)} <em>(${ev.index + 1}/${ev.total})</em></div>`;
|
||||
} else if (ev.phase === 'agent-done') {
|
||||
const o = ev.output || {};
|
||||
const body = (o.response || '').slice(0, 4000);
|
||||
card.innerHTML = `<div class="cph-head">${escAttr(ev.agentId)} 완료 <span class="cph-meta">${(o.durationMs/1000).toFixed(1)}s${o.error ? ' · ⚠️ ' + escAttr(o.error) : ''}</span></div>
|
||||
<div class="markdown-body">${fmt(body)}</div>`;
|
||||
} else if (ev.phase === 'report-start') {
|
||||
card.innerHTML = '<div class="cph-head">🧭 CEO 종합 보고서 작성 중…</div>';
|
||||
} else if (ev.phase === 'report-done') {
|
||||
card.className += ' report';
|
||||
card.innerHTML = `<div class="cph-head">🧭 CEO 보고서${ev.ok ? '' : ' (fallback)'}</div>
|
||||
<div class="markdown-body">${fmt(ev.report || '')}</div>`;
|
||||
} else if (ev.phase === 'session-saved') {
|
||||
card.innerHTML = `<div class="cph-meta">세션 저장 완료 — 클릭하여 열기</div>`;
|
||||
card.style.cursor = 'pointer';
|
||||
card.onclick = () => vscode.postMessage({ type: 'openCompanySession', sessionDir: ev.sessionDir });
|
||||
} else if (ev.phase === 'aborted') {
|
||||
card.innerHTML = `<div class="cph-head">⛔ 회사 모드 중단</div><div class="cph-meta">${escAttr(ev.reason)}</div>`;
|
||||
}
|
||||
chatEl.appendChild(card);
|
||||
chatEl.scrollTop = chatEl.scrollHeight;
|
||||
}
|
||||
|
||||
// ── Knowledge Mix: global slider ──────────────────────────────────────
|
||||
// Mirrors `g1nation.knowledgeMix.secondBrainWeight`. The hint label updates
|
||||
// live as the user drags; the value is committed (postMessage) on `change`
|
||||
|
||||
Reference in New Issue
Block a user