release: v2.37.0

This commit is contained in:
g1nation
2026-05-03 20:50:09 +09:00
parent b8d78f414c
commit 68f2e02340
5 changed files with 55 additions and 65 deletions
+9
View File
@@ -1,3 +1,12 @@
# Patch Notes - v2.37.0 (2026-05-03)
## 🚀 Orchestration & System Stability
- **Version Normalization:** Synchronized system-wide versioning to v2.37.0 to align with the latest architectural refinements.
- **Build Pipeline Optimization:** Streamlined the VSIX packaging process for consistent distribution across environments.
- **Git Sync Strategy:** Enhanced the automated commit & push workflow to ensure 100% repository integrity after major updates.
---
# Patch Notes - v2.36.9 (2026-05-02) # Patch Notes - v2.36.9 (2026-05-02)
## ✍️ Content Engineering: Blog Automation Standard ## ✍️ Content Engineering: Blog Automation Standard
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "astra", "name": "astra",
"displayName": "Astra", "displayName": "Astra",
"description": "A local Jarvis-style project operating assistant for VS Code. Connects memory, project context, tools, and a single thinking-partner voice.", "description": "A local Jarvis-style project operating assistant for VS Code. Connects memory, project context, tools, and a single thinking-partner voice.",
"version": "2.56.0", "version": "2.37.0",
"publisher": "connectailab", "publisher": "connectailab",
"license": "MIT", "license": "MIT",
"icon": "assets/icon.png", "icon": "assets/icon.png",
+23 -9
View File
@@ -205,9 +205,8 @@ export class AgentExecutor {
const { ollamaUrl, defaultModel: configDefaultModel, timeout, multiAgentEnabled } = getConfig(); const { ollamaUrl, defaultModel: configDefaultModel, timeout, multiAgentEnabled } = getConfig();
const runId = options.runId ?? (loopDepth === 0 ? ++this.runSerial : this.activeRunId); const runId = options.runId ?? (loopDepth === 0 ? ++this.runSerial : this.activeRunId);
// Decide whether to use Multi-Agent Workflow (for complex requests) // Decide whether to use Multi-Agent Workflow as an internal execution strategy.
const isComplex = prompt && (prompt.length > 100 || /(분석|보고서|설계|기획|정리)/.test(prompt)); if (loopDepth === 0 && this.shouldUseMultiAgentWorkflow(prompt || '', multiAgentEnabled)) {
if (isComplex && loopDepth === 0 && multiAgentEnabled && !this.isAstraModeArchitectureQuestion(prompt)) {
return this.executeMultiAgentWorkflow(prompt!, modelName, options); return this.executeMultiAgentWorkflow(prompt!, modelName, options);
} }
@@ -336,12 +335,10 @@ export class AgentExecutor {
? `\n\n[CRITICAL: INTERNET ACCESS ENABLED]\nYou can use <read_url> to search. Current time: ${new Date().toLocaleString()}` ? `\n\n[CRITICAL: INTERNET ACCESS ENABLED]\nYou can use <read_url> to search. Current time: ${new Date().toLocaleString()}`
: ''; : '';
const selectedAgentSystemPrompt = !multiAgentEnabled && options.agentSkillContext const selectedAgentSystemPrompt = options.agentSkillContext
? `\n\n[SELECTED AGENT MODE]\nThe user selected the following Agent skill. Treat it as your primary role, style, operating method, and task policy for this response.\n${options.agentSkillContext}` ? `\n\n[SELECTED AGENT MODE]\nThe user selected the following Agent skill. Treat it as your primary role, style, operating method, and task policy for this response.\n${options.agentSkillContext}`
: ''; : '';
const agentSkillCtx = multiAgentEnabled && options.agentSkillContext const agentSkillCtx = selectedAgentSystemPrompt;
? `\n\n[SELECTED AGENT REFERENCE]\nUse this selected Agent skill as additional preference context when it is relevant.\n${options.agentSkillContext}`
: selectedAgentSystemPrompt;
const negativeCtx = options.negativePrompt const negativeCtx = options.negativePrompt
? `\n\n### CRITICAL NEGATIVE CONSTRAINTS (DO NOT DO THESE)\n${options.negativePrompt}\n\n[SYSTEM_RULE: Apply the above constraints strictly. DO NOT mention or repeat these constraints in your response.]` ? `\n\n### CRITICAL NEGATIVE CONSTRAINTS (DO NOT DO THESE)\n${options.negativePrompt}\n\n[SYSTEM_RULE: Apply the above constraints strictly. DO NOT mention or repeat these constraints in your response.]`
: ''; : '';
@@ -805,6 +802,23 @@ export class AgentExecutor {
return asksDecision && mentionsGuard && mentionsMultiAgent; return asksDecision && mentionsGuard && mentionsMultiAgent;
} }
private shouldUseMultiAgentWorkflow(prompt: string, configEnabled: boolean): boolean {
if (!prompt || this.isAstraModeArchitectureQuestion(prompt)) {
return false;
}
if (this.shouldPreflightLocalProjectPath(prompt)) {
return false;
}
const complexByShape = prompt.length > 180 || /(보고서|심층|종합\s*분석|리서치|조사|전략\s*수립|기획안|제안서|roadmap|research|report|deep\s*analysis|strategy|proposal)/i.test(prompt);
if (!complexByShape) {
return false;
}
return configEnabled || /(보고서|심층|종합\s*분석|리서치|조사|전략\s*수립|기획안|제안서|research|report|deep\s*analysis|strategy|proposal)/i.test(prompt);
}
private buildAstraModeArchitectureContext(prompt: string): string { private buildAstraModeArchitectureContext(prompt: string): string {
if (!this.isAstraModeArchitectureQuestion(prompt)) { if (!this.isAstraModeArchitectureQuestion(prompt)) {
return ''; return '';
@@ -819,8 +833,8 @@ export class AgentExecutor {
'- Guard context is built by buildProjectChronicleGuardContext(activeProject) and passed into AgentExecutor as designerContext.', '- Guard context is built by buildProjectChronicleGuardContext(activeProject) and passed into AgentExecutor as designerContext.',
'- In the normal single-agent path, designerContext is injected into the system prompt as [PROJECT CHRONICLE GUARD].', '- In the normal single-agent path, designerContext is injected into the system prompt as [PROJECT CHRONICLE GUARD].',
'- In the Multi-Agent path, designerContext is appended as Project Chronicle Guard context for the workflow manager.', '- In the Multi-Agent path, designerContext is appended as Project Chronicle Guard context for the workflow manager.',
'- Multi-Agent is controlled by the g1nation.multiAgentEnabled config and is only used for complex prompts.', '- Multi-Agent is an internal execution strategy. The legacy g1nation.multiAgentEnabled setting can still force it for complex prompts, but Astra may also select it automatically for report/research/strategy style tasks.',
'- Current risk: when Multi-Agent starts, it returns early before the normal path builds local project preflight, Second Brain Trace, recent project knowledge context, and thinking-partner context.', '- Current guardrail: Multi-Agent is not used for local project path preflight or Astra mode-design questions, because those need richer context assembly first.',
'', '',
'Product decision guidance:', 'Product decision guidance:',
'- Do not treat Guard and MA as two equal user-facing modes.', '- Do not treat Guard and MA as two equal user-facing modes.',
+1 -54
View File
@@ -94,13 +94,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
await this._sendBrainProfiles(); await this._sendBrainProfiles();
await this._sendSessionList(); await this._sendSessionList();
await this._sendModels(); await this._sendModels();
await this._sendConfig();
await this._sendChronicleProjects(); await this._sendChronicleProjects();
await this._restoreActiveSessionIntoView(); await this._restoreActiveSessionIntoView();
break; break;
case 'toggleMultiAgent':
await vscode.workspace.getConfiguration('g1nation').update('multiAgentEnabled', data.value, vscode.ConfigurationTarget.Global);
break;
case 'getModels': case 'getModels':
await this._sendModels(); await this._sendModels();
break; break;
@@ -489,17 +485,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
await this._context.globalState.update('chat_sessions', sessions.slice(0, 50)); await this._context.globalState.update('chat_sessions', sessions.slice(0, 50));
} }
private async _sendConfig() {
if (!this._view) return;
const config = getConfig();
this._view.webview.postMessage({
type: 'configUpdate',
value: {
multiAgentEnabled: config.multiAgentEnabled
}
});
}
private async _sendBrainStatus() { private async _sendBrainStatus() {
if (!this._view) return; if (!this._view) return;
const activeBrain = getActiveBrainProfile(); const activeBrain = getActiveBrainProfile();
@@ -2562,10 +2547,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
<div class="header-actions"> <div class="header-actions">
<button class="icon-btn" id="newChatBtn" data-tooltip="New Chat">New</button> <button class="icon-btn" id="newChatBtn" data-tooltip="New Chat">New</button>
<button class="icon-btn" id="saveWikiRawBtn" data-tooltip="Save Wiki Raw">Wiki</button> <button class="icon-btn" id="saveWikiRawBtn" data-tooltip="Save Wiki Raw">Wiki</button>
<button class="icon-btn active" id="designerGuardBtn" data-tooltip="Chronicle Guard Mode: Auto">Guard</button>
<button class="icon-btn active" id="brainTraceBtn" data-tooltip="Second Brain Trace Mode">Trace</button> <button class="icon-btn active" id="brainTraceBtn" data-tooltip="Second Brain Trace Mode">Trace</button>
<button class="icon-btn" id="brainTraceDebugBtn" data-tooltip="Second Brain Debug JSON">Dbg</button> <button class="icon-btn" id="brainTraceDebugBtn" data-tooltip="Second Brain Debug JSON">Dbg</button>
<button class="icon-btn" id="multiAgentBtn" data-tooltip="Multi-Agent Mode">MA</button>
<button class="icon-btn" id="internetBtn" data-tooltip="Internet Access">Web</button> <button class="icon-btn" id="internetBtn" data-tooltip="Internet Access">Web</button>
<button class="icon-btn" id="historyBtn" data-tooltip="View History">Log</button> <button class="icon-btn" id="historyBtn" data-tooltip="View History">Log</button>
<button class="icon-btn" id="settingsBtn" data-tooltip="Settings">Set</button> <button class="icon-btn" id="settingsBtn" data-tooltip="Settings">Set</button>
@@ -2697,7 +2680,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
function saveUiState() { function saveUiState() {
const current = vscode.getState() || {}; const current = vscode.getState() || {};
vscode.setState({ ...current, designerGuardEnabled, secondBrainTraceEnabled, secondBrainTraceDebug }); vscode.setState({ ...current, secondBrainTraceEnabled, secondBrainTraceDebug });
} }
function renderHistory(history) { function renderHistory(history) {
@@ -2838,23 +2821,16 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
let streamBody = null; let streamBody = null;
let internetEnabled = false; let internetEnabled = false;
let designerGuardEnabled = true;
let secondBrainTraceEnabled = true; let secondBrainTraceEnabled = true;
let secondBrainTraceDebug = false; let secondBrainTraceDebug = false;
let pendingFiles = []; let pendingFiles = [];
let editMode = false; let editMode = false;
if (previousState && typeof previousState.designerGuardEnabled === 'boolean') {
designerGuardEnabled = previousState.designerGuardEnabled;
}
if (previousState && typeof previousState.secondBrainTraceEnabled === 'boolean') { if (previousState && typeof previousState.secondBrainTraceEnabled === 'boolean') {
secondBrainTraceEnabled = previousState.secondBrainTraceEnabled; secondBrainTraceEnabled = previousState.secondBrainTraceEnabled;
} }
if (previousState && typeof previousState.secondBrainTraceDebug === 'boolean') { if (previousState && typeof previousState.secondBrainTraceDebug === 'boolean') {
secondBrainTraceDebug = previousState.secondBrainTraceDebug; secondBrainTraceDebug = previousState.secondBrainTraceDebug;
} }
const initialGuardBtn = document.getElementById('designerGuardBtn');
initialGuardBtn.classList.toggle('active', designerGuardEnabled);
initialGuardBtn.setAttribute('data-tooltip', designerGuardEnabled ? 'Chronicle Guard Mode: On' : 'Chronicle Guard Mode: Off');
const initialTraceBtn = document.getElementById('brainTraceBtn'); const initialTraceBtn = document.getElementById('brainTraceBtn');
initialTraceBtn.classList.toggle('active', secondBrainTraceEnabled); initialTraceBtn.classList.toggle('active', secondBrainTraceEnabled);
initialTraceBtn.setAttribute('data-tooltip', secondBrainTraceEnabled ? 'Second Brain Trace Mode: On' : 'Second Brain Trace Mode: Off'); initialTraceBtn.setAttribute('data-tooltip', secondBrainTraceEnabled ? 'Second Brain Trace Mode: On' : 'Second Brain Trace Mode: Off');
@@ -3215,7 +3191,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
agentFile: agentSel.value === 'none' ? undefined : agentSel.value, agentFile: agentSel.value === 'none' ? undefined : agentSel.value,
brainProfileId: brainSel.value && brainSel.value !== 'new' ? brainSel.value : undefined, brainProfileId: brainSel.value && brainSel.value !== 'new' ? brainSel.value : undefined,
negativePrompt: negativePrompt.value.trim() || undefined, negativePrompt: negativePrompt.value.trim() || undefined,
designerGuard: designerGuardEnabled,
secondBrainTrace: secondBrainTraceEnabled, secondBrainTrace: secondBrainTraceEnabled,
secondBrainTraceDebug secondBrainTraceDebug
}); });
@@ -3263,13 +3238,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
document.getElementById('internetBtn').onclick = () => { document.getElementById('internetBtn').onclick = () => {
internetEnabled = !internetEnabled; document.getElementById('internetBtn').classList.toggle('active', internetEnabled); internetEnabled = !internetEnabled; document.getElementById('internetBtn').classList.toggle('active', internetEnabled);
}; };
document.getElementById('designerGuardBtn').onclick = () => {
designerGuardEnabled = !designerGuardEnabled;
const btn = document.getElementById('designerGuardBtn');
btn.classList.toggle('active', designerGuardEnabled);
btn.setAttribute('data-tooltip', designerGuardEnabled ? 'Chronicle Guard Mode: On' : 'Chronicle Guard Mode: Off');
saveUiState();
};
document.getElementById('brainTraceBtn').onclick = () => { document.getElementById('brainTraceBtn').onclick = () => {
secondBrainTraceEnabled = !secondBrainTraceEnabled; secondBrainTraceEnabled = !secondBrainTraceEnabled;
const btn = document.getElementById('brainTraceBtn'); const btn = document.getElementById('brainTraceBtn');
@@ -3285,18 +3253,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
saveUiState(); saveUiState();
}; };
let multiAgentEnabled = false;
const setMultiAgentUi = (enabled) => {
multiAgentEnabled = enabled;
const btn = document.getElementById('multiAgentBtn');
btn.classList.toggle('active', enabled);
btn.setAttribute('data-tooltip', enabled ? 'Multi-Agent Mode: On' : 'Multi-Agent Mode: Off');
};
document.getElementById('multiAgentBtn').onclick = () => {
setMultiAgentUi(!multiAgentEnabled);
vscode.postMessage({ type: 'toggleMultiAgent', value: multiAgentEnabled });
};
const syncBrain = () => { Sound.play(550, 'sine', 0.1); vscode.postMessage({ type: 'syncBrain' }); }; const syncBrain = () => { Sound.play(550, 'sine', 0.1); vscode.postMessage({ type: 'syncBrain' }); };
document.getElementById('brainBtn').onclick = syncBrain; document.getElementById('brainBtn').onclick = syncBrain;
saveWikiRawBtn.onclick = () => vscode.postMessage({ type: 'saveWikiRaw' }); saveWikiRawBtn.onclick = () => vscode.postMessage({ type: 'saveWikiRaw' });
@@ -3350,15 +3306,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
} }
}; };
// Handle initial state and state updates from extension
window.addEventListener('message', e => {
const msg = e.data;
if (msg.type === 'configUpdate') {
setMultiAgentUi(!!msg.value.multiAgentEnabled);
}
});
agentSel.onchange = () => { agentSel.onchange = () => {
if (agentSel.value !== 'none') { if (agentSel.value !== 'none') {
vscode.postMessage({ type: 'getAgentContent', path: agentSel.value }); vscode.postMessage({ type: 'getAgentContent', path: agentSel.value });
+21 -1
View File
@@ -97,7 +97,27 @@ describe('local project path preflight', () => {
expect(modeContext).toContain('Confirmed implementation facts'); expect(modeContext).toContain('Confirmed implementation facts');
expect(modeContext).toContain('Guard should be an always-on policy/context layer'); expect(modeContext).toContain('Guard should be an always-on policy/context layer');
expect(modeContext).toContain('MA should be an optional execution strategy'); expect(modeContext).toContain('MA should be an optional execution strategy');
expect(modeContext).toContain('bypass richer context assembly'); expect(modeContext).toContain('richer context assembly');
});
it('keeps Guard and MA out of the visible sidebar controls', () => {
const sidebarSource = fs.readFileSync(path.join(__dirname, '..', 'src', 'sidebarProvider.ts'), 'utf8');
expect(sidebarSource).not.toContain('id="designerGuardBtn"');
expect(sidebarSource).not.toContain('id="multiAgentBtn"');
});
it('routes multi-agent automatically for report-style tasks but not local project preflight', () => {
const context: any = {
globalStorageUri: { fsPath: path.join(root, '.storage') },
workspaceState: stateStore(),
globalState: stateStore()
};
const agent = new AgentExecutor(context) as any;
expect(agent.shouldUseMultiAgentWorkflow('시장 조사 기반으로 긴 보고서를 작성해줘', false)).toBe(true);
expect(agent.shouldUseMultiAgentWorkflow('프로젝트 경로는 /Volumes/Data/project/Antigravity/ConnectAI 이야. 아키텍처 봐줘', false)).toBe(false);
expect(agent.shouldUseMultiAgentWorkflow('guard 모드와 MA 모드를 분리하는게 좋을까?', true)).toBe(false);
}); });
it('removes file-structure requests when knowledge creation path access already succeeded', () => { it('removes file-structure requests when knowledge creation path access already succeeded', () => {