From ac902c18414f5261804856e295f330462da5c068 Mon Sep 17 00:00:00 2001 From: g1nation Date: Fri, 1 May 2026 19:41:00 +0900 Subject: [PATCH] fix: chat history persistence and webview state restoration --- PATCHNOTES.md | 9 +++++++ package.json | 2 +- src/agent.ts | 5 ++-- src/sidebarProvider.ts | 55 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/PATCHNOTES.md b/PATCHNOTES.md index a391cce..5783719 100644 --- a/PATCHNOTES.md +++ b/PATCHNOTES.md @@ -1,3 +1,12 @@ +# Patch Notes - v2.33.6 (2026-05-01) + +## 🛠️ Critical Bug Fixes +- **Chat Persistence Overhaul:** Resolved an issue where chat history disappeared after switching sidebar tabs. +- **Webview State Management:** Implemented `vscode.setState` for instant UI restoration. +- **Internal Message Logic:** Refined message filtering to ensure intermediate agent steps are visible to the user. + +--- + # Patch Notes - v2.33.5 (2026-05-01) ## 🎨 UI Refinement diff --git a/package.json b/package.json index a93e86f..b24194d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "g1nation", "displayName": "G1nation", "description": "High-performance autonomous local AI coding agent for VS Code. Features vectorized inference, asynchronous task management, and 100% offline processing.", - "version": "2.33.5", + "version": "2.33.6", "publisher": "connectailab", "license": "MIT", "icon": "assets/icon.png", diff --git a/src/agent.ts b/src/agent.ts index c68c665..80e1dd0 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -119,7 +119,7 @@ export class AgentExecutor { } public getHistory() { - return this.chatHistory.filter(message => !message.internal); + return this.chatHistory.filter(message => !message.internal || message.role === 'assistant'); } public setHistory(history: ChatMessage[]) { @@ -394,7 +394,7 @@ export class AgentExecutor { // 5. Execute Actions const rationale = this.parseRationale(aiResponseText); - const assistantMessage: ChatMessage = { role: 'assistant', content: aiResponseText, internal: true, rationale }; + const assistantMessage: ChatMessage = { role: 'assistant', content: aiResponseText, internal: false, rationale }; this.chatHistory.push(assistantMessage); this.statusBarManager.updateStatus(AgentStatus.Executing); @@ -443,7 +443,6 @@ export class AgentExecutor { return; } - assistantMessage.internal = false; this.emitHistoryChanged(); this.statusBarManager.updateStatus(AgentStatus.Success); this.webview.postMessage({ type: 'streamChunk', value: aiResponseText }); diff --git a/src/sidebarProvider.ts b/src/sidebarProvider.ts index df3cb61..ff59e88 100644 --- a/src/sidebarProvider.ts +++ b/src/sidebarProvider.ts @@ -200,7 +200,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn if (!this._view) return; const blankChatActive = this._context.globalState.get(SidebarChatProvider.blankChatStateKey, false); - if (blankChatActive) { + const currentHistory = this._agent.getHistory(); + + if (blankChatActive && currentHistory.length === 0) { return; } @@ -212,7 +214,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn await this._context.globalState.update(SidebarChatProvider.activeSessionStateKey, null); } - const currentHistory = this._agent.getHistory(); if (currentHistory.length > 0) { this._view.webview.postMessage({ type: 'restoreHistory', value: currentHistory }); await this._persistLastVisibleChat(currentHistory); @@ -1893,6 +1894,29 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn const vscode = acquireVsCodeApi(); const chat = document.getElementById('chat'); const input = document.getElementById('input'); + + // [State Persistence - Tier 0] 즉시 복원 (Instant Restore from WebView State) + const previousState = vscode.getState(); + if (previousState && previousState.history && previousState.history.length > 0) { + console.log('[G1nation] Restoring from Webview State...'); + renderHistory(previousState.history); + } + + function saveWebviewState(history) { + vscode.setState({ history }); + } + + function renderHistory(history) { + if (!history || history.length === 0) return; + chat.innerHTML = ''; + history.forEach(m => { + if (!m) return; + // Only skip truly internal system messages, keep assistant thoughts + if (m.role === 'system' && m.internal) return; + addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user', m.rationale); + }); + chat.scrollTop = chat.scrollHeight; + } const sendBtn = document.getElementById('sendBtn'); const stopBtn = document.getElementById('stopBtn'); const cancelBtn = document.getElementById('cancelBtn'); @@ -2112,6 +2136,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn switch(msg.type) { case 'addMessage': addMsg(msg.value, msg.role, msg.rationale); + // Update state for non-streamed messages + const s = vscode.getState() || { history: [] }; + s.history.push({ role: msg.role === 'assistant' ? 'assistant' : 'user', content: msg.value, rationale: msg.rationale }); + saveWebviewState(s.history); break; case 'streamStart': thinkingBar.classList.remove('active'); @@ -2128,9 +2156,15 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn } break; case 'streamEnd': - if (streamBody) streamBody.classList.remove('stream-active'); + if (streamBody) { + streamBody.classList.remove('stream-active'); + // Update state after stream finishes + const state = vscode.getState() || { history: [] }; + state.history.push({ role: 'assistant', content: streamBody._parent._raw }); + saveWebviewState(state.history); + } streamBody = null; - // \uc0dd\uc131 \uc644\ub8cc \uc2dc Stop \ubc84\ud2bc \uc228\uae30\uace0 Send \ubcf5\uad50 + // 생성 완료 시 Stop 버튼 숨기고 Send 복구 setGenerating(false); resetStepper(); Sound.success(); @@ -2143,12 +2177,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn : (Array.isArray(historyPayload?.history) ? historyPayload.history : []); if (history && history.length > 0) { - chat.innerHTML = ''; - history.forEach(m => { - if (!m || m.internal) return; - addMsg(m.content, m.role === 'assistant' ? 'assistant' : 'user', m.rationale); - }); - chat.scrollTop = chat.scrollHeight; + renderHistory(history); + saveWebviewState(history); } if (historyPayload?.negativePrompt !== undefined) { negativePrompt.value = historyPayload.negativePrompt; @@ -2358,6 +2388,11 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn setDraftActive(false); setGenerating(true); thinkingBar.classList.add('active'); + + // Save state after sending + const currentState = vscode.getState() || { history: [] }; + currentState.history.push({ role: 'user', content: val }); + saveWebviewState(currentState.history); } sendBtn.onclick = send;