From 1acc24bc175f86de8836d0bd257230f8384dd8d6 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Sun, 30 Nov 2025 22:26:04 +0800 Subject: [PATCH] fix(vscode-ide-companion): Interactive unification of first login and login --- .../src/agents/qwenAgentManager.ts | 33 +- .../src/agents/qwenConnectionHandler.ts | 49 ++- .../vscode-ide-companion/src/webview/App.tsx | 12 +- .../src/webview/WebViewProvider.ts | 341 ++++-------------- .../messages/MessageOrdering.test.tsx | 88 ----- .../webview/handlers/SessionMessageHandler.ts | 132 ++++++- 6 files changed, 273 insertions(+), 382 deletions(-) delete mode 100644 packages/vscode-ide-companion/src/webview/components/messages/MessageOrdering.test.tsx diff --git a/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts b/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts index bd899be6..2538908a 100644 --- a/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts +++ b/packages/vscode-ide-companion/src/agents/qwenAgentManager.ts @@ -686,7 +686,38 @@ export class QwenAgentManager { ); } - await this.connection.newSession(workingDir); + // Try to create a new ACP session. If the backend asks for auth despite our + // cached flag (e.g. fresh process or expired tokens), re-authenticate and retry. + try { + await this.connection.newSession(workingDir); + } catch (err) { + const msg = err instanceof Error ? err.message : String(err); + const requiresAuth = + msg.includes('Authentication required') || + msg.includes('(code: -32000)'); + + if (requiresAuth) { + console.warn( + '[QwenAgentManager] session/new requires authentication. Retrying with authenticate...', + ); + try { + await this.connection.authenticate(authMethod); + // Persist auth cache so subsequent calls can skip the web flow. + if (effectiveAuth) { + await effectiveAuth.saveAuthState(workingDir, authMethod); + } + await this.connection.newSession(workingDir); + } catch (reauthErr) { + // Clear potentially stale cache on failure and rethrow + if (effectiveAuth) { + await effectiveAuth.clearAuthState(); + } + throw reauthErr; + } + } else { + throw err; + } + } const newSessionId = this.connection.currentSessionId; console.log( '[QwenAgentManager] New session created with ID:', diff --git a/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts b/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts index a8480d1c..23c77875 100644 --- a/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts +++ b/packages/vscode-ide-companion/src/agents/qwenConnectionHandler.ts @@ -39,10 +39,7 @@ export class QwenConnectionHandler { cliPath?: string, ): Promise { const connectId = Date.now(); - console.log(`\n========================================`); console.log(`[QwenAgentManager] 🚀 CONNECT() CALLED - ID: ${connectId}`); - console.log(`[QwenAgentManager] Call stack:\n${new Error().stack}`); - console.log(`========================================\n`); // Check CLI version and features const cliVersionManager = CliVersionManager.getInstance(); @@ -166,7 +163,9 @@ export class QwenConnectionHandler { // Create new session if unable to restore if (!sessionRestored) { - console.log('[QwenAgentManager] Creating new session...'); + console.log( + '[QwenAgentManager] no sessionRestored, Creating new session...', + ); // Check if we have valid cached authentication let hasValidAuth = false; @@ -217,7 +216,13 @@ export class QwenConnectionHandler { console.log( '[QwenAgentManager] Creating new session after authentication...', ); - await this.newSessionWithRetry(connection, workingDir, 3); + await this.newSessionWithRetry( + connection, + workingDir, + 3, + authMethod, + authStateManager, + ); console.log('[QwenAgentManager] New session created successfully'); // Ensure auth state is saved (prevent repeated authentication) @@ -257,6 +262,8 @@ export class QwenConnectionHandler { connection: AcpConnection, workingDir: string, maxRetries: number, + authMethod: string, + authStateManager?: AuthStateManager, ): Promise { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { @@ -274,6 +281,38 @@ export class QwenConnectionHandler { errorMessage, ); + // If the backend reports that authentication is required, try to + // authenticate on-the-fly once and retry without waiting. + const requiresAuth = + errorMessage.includes('Authentication required') || + errorMessage.includes('(code: -32000)'); + if (requiresAuth) { + console.log( + '[QwenAgentManager] Backend requires authentication. Authenticating and retrying session/new...', + ); + try { + await connection.authenticate(authMethod); + if (authStateManager) { + await authStateManager.saveAuthState(workingDir, authMethod); + } + // Retry immediately after successful auth + await connection.newSession(workingDir); + console.log( + '[QwenAgentManager] Session created successfully after auth', + ); + return; + } catch (authErr) { + console.error( + '[QwenAgentManager] Re-authentication failed:', + authErr, + ); + if (authStateManager) { + await authStateManager.clearAuthState(); + } + // Fall through to retry logic below + } + } + if (attempt === maxRetries) { throw new Error( `Session creation failed after ${maxRetries} attempts: ${errorMessage}`, diff --git a/packages/vscode-ide-companion/src/webview/App.tsx b/packages/vscode-ide-companion/src/webview/App.tsx index 8186ceb7..cfb8c34e 100644 --- a/packages/vscode-ide-companion/src/webview/App.tsx +++ b/packages/vscode-ide-companion/src/webview/App.tsx @@ -60,10 +60,16 @@ export const App: React.FC = () => { toolCall: PermissionToolCall; } | null>(null); const [planEntries, setPlanEntries] = useState([]); - const messagesEndRef = useRef(null); + const messagesEndRef = useRef( + null, + ) as React.RefObject; // Scroll container for message list; used to keep the view anchored to the latest content - const messagesContainerRef = useRef(null); - const inputFieldRef = useRef(null); + const messagesContainerRef = useRef( + null, + ) as React.RefObject; + const inputFieldRef = useRef( + null, + ) as React.RefObject; const [showBanner, setShowBanner] = useState(true); const [editMode, setEditMode] = useState('ask'); const [thinkingEnabled, setThinkingEnabled] = useState(false); diff --git a/packages/vscode-ide-companion/src/webview/WebViewProvider.ts b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts index 54be3f64..fc68997d 100644 --- a/packages/vscode-ide-companion/src/webview/WebViewProvider.ts +++ b/packages/vscode-ide-companion/src/webview/WebViewProvider.ts @@ -309,57 +309,13 @@ export class WebViewProvider { }); } - // // Initialize empty conversation immediately for fast UI rendering - // await this.initializeEmptyConversation(); - - // // Perform background CLI detection and connection without blocking UI - // this.performBackgroundInitialization(); - - // Smart login restore: Check if we have valid cached auth and restore connection if available - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); - const config = vscode.workspace.getConfiguration('qwenCode'); - const openaiApiKey = config.get('qwen.openaiApiKey', ''); - const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; - - // Check if we have valid cached authentication - let hasValidAuth = false; - if (this.authStateManager) { - hasValidAuth = await this.authStateManager.hasValidAuth( - workingDir, - authMethod, - ); - console.log( - '[WebViewProvider] Has valid cached auth on show:', - hasValidAuth, - ); - } - - if (hasValidAuth && !this.agentInitialized) { - console.log( - '[WebViewProvider] Found valid cached auth, attempting to restore connection...', - ); - try { - await this.initializeAgentConnection(); - console.log('[WebViewProvider] Connection restored successfully'); - } catch (error) { - console.error('[WebViewProvider] Failed to restore connection:', error); - // Fall back to empty conversation if restore fails - await this.initializeEmptyConversation(); - } - } else if (this.agentInitialized) { - console.log( - '[WebViewProvider] Agent already initialized, reusing existing connection', - ); - // Reload current session messages - await this.loadCurrentSessionMessages(); - } else { - console.log( - '[WebViewProvider] No valid cached auth or agent already initialized, showing empty conversation', - ); - // Just initialize empty conversation for the UI - await this.initializeEmptyConversation(); - } + // Lazy initialization: Do not attempt to connect/auth on WebView show. + // Render the chat UI immediately; we will connect/login on-demand when the + // user sends a message or requests a session action. + console.log( + '[WebViewProvider] Lazy init: rendering empty conversation only', + ); + await this.initializeEmptyConversation(); } /** @@ -459,124 +415,6 @@ export class WebViewProvider { } } - /** - * Perform background initialization without blocking UI - * This method runs CLI detection and connection in the background - */ - private async performBackgroundInitialization(): Promise { - try { - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); - const config = vscode.workspace.getConfiguration('qwenCode'); - const qwenEnabled = config.get('qwen.enabled', true); - - if (qwenEnabled) { - // Check if we have valid cached authentication - const openaiApiKey = config.get('qwen.openaiApiKey', ''); - const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; - - let hasValidAuth = false; - if (this.authStateManager) { - hasValidAuth = await this.authStateManager.hasValidAuth( - workingDir, - authMethod, - ); - console.log( - '[WebViewProvider] Has valid cached auth in background init:', - hasValidAuth, - ); - } - - // Perform CLI detection in background - const cliDetection = await CliDetector.detectQwenCli(); - - if (!cliDetection.isInstalled) { - console.log( - '[WebViewProvider] Qwen CLI not detected in background check', - ); - console.log( - '[WebViewProvider] CLI detection error:', - cliDetection.error, - ); - - // Notify webview that CLI is not installed - this.sendMessageToWebView({ - type: 'cliNotInstalled', - data: { - error: cliDetection.error, - }, - }); - } else { - console.log( - '[WebViewProvider] Qwen CLI detected in background check, attempting connection...', - ); - console.log('[WebViewProvider] CLI path:', cliDetection.cliPath); - console.log('[WebViewProvider] CLI version:', cliDetection.version); - - if (hasValidAuth && !this.agentInitialized) { - console.log( - '[WebViewProvider] Found valid cached auth, attempting to restore connection in background...', - ); - try { - // Pass the detected CLI path to ensure we use the correct installation - await this.agentManager.connect( - workingDir, - this.authStateManager, - cliDetection.cliPath, - ); - console.log( - '[WebViewProvider] Connection restored successfully in background', - ); - this.agentInitialized = true; - - // Load messages from the current Qwen session - await this.loadCurrentSessionMessages(); - - // Notify webview that agent is connected - this.sendMessageToWebView({ - type: 'agentConnected', - data: {}, - }); - } catch (error) { - console.error( - '[WebViewProvider] Failed to restore connection in background:', - error, - ); - // Clear auth cache on error - await this.authStateManager.clearAuthState(); - - // Notify webview that agent connection failed - this.sendMessageToWebView({ - type: 'agentConnectionError', - data: { - message: - error instanceof Error ? error.message : String(error), - }, - }); - } - } else if (this.agentInitialized) { - console.log( - '[WebViewProvider] Agent already initialized, no need to reconnect in background', - ); - } else { - console.log( - '[WebViewProvider] No valid cached auth, skipping background connection', - ); - } - } - } else { - console.log( - '[WebViewProvider] Qwen agent is disabled in settings (background)', - ); - } - } catch (error) { - console.error( - '[WebViewProvider] Background initialization failed:', - error, - ); - } - } - /** * Force re-login by clearing auth cache and reconnecting * Called when user explicitly uses /login command @@ -588,55 +426,72 @@ export class WebViewProvider { !!this.authStateManager, ); - // Clear existing auth cache - if (this.authStateManager) { - await this.authStateManager.clearAuthState(); - console.log('[WebViewProvider] Auth cache cleared'); - } else { - console.log('[WebViewProvider] No authStateManager to clear'); - } + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: 'Logging in to Qwen Code... ', + cancellable: false, + }, + async (progress) => { + try { + progress.report({ message: 'Preparing sign-in...' }); - // Disconnect existing connection if any - if (this.agentInitialized) { - try { - this.agentManager.disconnect(); - console.log('[WebViewProvider] Existing connection disconnected'); - } catch (error) { - console.log('[WebViewProvider] Error disconnecting:', error); - } - this.agentInitialized = false; - } + // Clear existing auth cache + if (this.authStateManager) { + await this.authStateManager.clearAuthState(); + console.log('[WebViewProvider] Auth cache cleared'); + } else { + console.log('[WebViewProvider] No authStateManager to clear'); + } - // Wait a moment for cleanup to complete - await new Promise((resolve) => setTimeout(resolve, 500)); + // Disconnect existing connection if any + if (this.agentInitialized) { + try { + this.agentManager.disconnect(); + console.log('[WebViewProvider] Existing connection disconnected'); + } catch (error) { + console.log('[WebViewProvider] Error disconnecting:', error); + } + this.agentInitialized = false; + } - // Reinitialize connection (will trigger fresh authentication) - try { - await this.initializeAgentConnection(); - console.log('[WebViewProvider] Force re-login completed successfully'); + // Wait a moment for cleanup to complete + await new Promise((resolve) => setTimeout(resolve, 300)); - // Send success notification to WebView - this.sendMessageToWebView({ - type: 'loginSuccess', - data: { message: 'Successfully logged in!' }, - }); - } catch (error) { - console.error('[WebViewProvider] Force re-login failed:', error); - console.error( - '[WebViewProvider] Error stack:', - error instanceof Error ? error.stack : 'N/A', - ); + progress.report({ + message: 'Connecting to CLI and starting sign-in...', + }); - // Send error notification to WebView - this.sendMessageToWebView({ - type: 'loginError', - data: { - message: `Login failed: ${error instanceof Error ? error.message : String(error)}`, - }, - }); + // Reinitialize connection (will trigger fresh authentication) + await this.initializeAgentConnection(); + console.log( + '[WebViewProvider] Force re-login completed successfully', + ); - throw error; - } + // Send success notification to WebView + this.sendMessageToWebView({ + type: 'loginSuccess', + data: { message: 'Successfully logged in!' }, + }); + } catch (error) { + console.error('[WebViewProvider] Force re-login failed:', error); + console.error( + '[WebViewProvider] Error stack:', + error instanceof Error ? error.stack : 'N/A', + ); + + // Send error notification to WebView + this.sendMessageToWebView({ + type: 'loginError', + data: { + message: `Login failed: ${error instanceof Error ? error.message : String(error)}`, + }, + }); + + throw error; + } + }, + ); } /** @@ -873,63 +728,11 @@ export class WebViewProvider { console.log('[WebViewProvider] Panel restored successfully'); - // TODO: - // await this.initializeEmptyConversation(); - // // Perform background initialization without blocking UI - // this.performBackgroundInitialization(); - - // Smart login restore: Check if we have valid cached auth and restore connection if available - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); - const config = vscode.workspace.getConfiguration('qwenCode'); - const openaiApiKey = config.get('qwen.openaiApiKey', ''); - const authMethod = openaiApiKey ? 'openai' : 'qwen-oauth'; - - // Check if we have valid cached authentication - let hasValidAuth = false; - if (this.authStateManager) { - hasValidAuth = await this.authStateManager.hasValidAuth( - workingDir, - authMethod, - ); - console.log( - '[WebViewProvider] Has valid cached auth on restore:', - hasValidAuth, - ); - } - - if (hasValidAuth && !this.agentInitialized) { - console.log( - '[WebViewProvider] Found valid cached auth, attempting to restore connection...', - ); - try { - await this.initializeAgentConnection(); - console.log('[WebViewProvider] Connection restored successfully'); - } catch (error) { - console.error('[WebViewProvider] Failed to restore connection:', error); - // Fall back to empty conversation if restore fails - await this.initializeEmptyConversation(); - } - } else if (this.agentInitialized) { - console.log( - '[WebViewProvider] Agent already initialized, refreshing connection...', - ); - try { - await this.refreshConnection(); - console.log('[WebViewProvider] Connection refreshed successfully'); - } catch (error) { - console.error('[WebViewProvider] Failed to refresh connection:', error); - // Fall back to empty conversation if refresh fails - this.agentInitialized = false; - await this.initializeEmptyConversation(); - } - } else { - console.log( - '[WebViewProvider] No valid cached auth or agent already initialized, showing empty conversation', - ); - // Just initialize empty conversation for the UI - await this.initializeEmptyConversation(); - } + // Lazy init on restore as well: do not auto-connect; just render UI. + console.log( + '[WebViewProvider] Lazy restore: rendering empty conversation only', + ); + await this.initializeEmptyConversation(); } /** diff --git a/packages/vscode-ide-companion/src/webview/components/messages/MessageOrdering.test.tsx b/packages/vscode-ide-companion/src/webview/components/messages/MessageOrdering.test.tsx deleted file mode 100644 index 95cf4c9f..00000000 --- a/packages/vscode-ide-companion/src/webview/components/messages/MessageOrdering.test.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/** - * @license - * Copyright 2025 Qwen Team - * SPDX-License-Identifier: Apache-2.0 - */ - -// Use explicit Vitest imports instead of relying on globals. -import { describe, it, expect } from 'vitest'; -import type { ToolCallData } from '../toolcalls/shared/types.js'; -import { hasToolCallOutput } from '../toolcalls/shared/utils.js'; - -describe('Message Ordering', () => { - it('should correctly identify tool calls with output', () => { - // Test failed tool call (should show) - const failedToolCall: ToolCallData = { - toolCallId: 'test-1', - kind: 'read', - title: 'Read file', - status: 'failed', - timestamp: 1000, - }; - expect(hasToolCallOutput(failedToolCall)).toBe(true); - - // Test execute tool call with title (should show) - const executeToolCall: ToolCallData = { - toolCallId: 'test-2', - kind: 'execute', - title: 'ls -la', - status: 'completed', - timestamp: 2000, - }; - expect(hasToolCallOutput(executeToolCall)).toBe(true); - - // Test tool call with content (should show) - const contentToolCall: ToolCallData = { - toolCallId: 'test-3', - kind: 'read', - title: 'Read file', - status: 'completed', - content: [ - { - type: 'content', - content: { - type: 'text', - text: 'File content', - }, - }, - ], - timestamp: 3000, - }; - expect(hasToolCallOutput(contentToolCall)).toBe(true); - - // Test tool call with locations (should show) - const locationToolCall: ToolCallData = { - toolCallId: 'test-4', - kind: 'read', - title: 'Read file', - status: 'completed', - locations: [ - { - path: '/path/to/file.txt', - }, - ], - timestamp: 4000, - }; - expect(hasToolCallOutput(locationToolCall)).toBe(true); - - // Test tool call with title (should show) - const titleToolCall: ToolCallData = { - toolCallId: 'test-5', - kind: 'generic', - title: 'Generic tool call', - status: 'completed', - timestamp: 5000, - }; - expect(hasToolCallOutput(titleToolCall)).toBe(true); - - // Test tool call without output (should not show) - const noOutputToolCall: ToolCallData = { - toolCallId: 'test-6', - kind: 'generic', - title: '', - status: 'completed', - timestamp: 6000, - }; - expect(hasToolCallOutput(noOutputToolCall)).toBe(false); - }); -}); diff --git a/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts b/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts index 1a6c1867..54b56614 100644 --- a/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts +++ b/packages/vscode-ide-companion/src/webview/handlers/SessionMessageHandler.ts @@ -376,17 +376,23 @@ export class SessionMessageHandler extends BaseMessageHandler { // Check for session not found error and handle it appropriately if ( errorMsg.includes('Session not found') || - errorMsg.includes('No active ACP session') + errorMsg.includes('No active ACP session') || + errorMsg.includes('Authentication required') || + errorMsg.includes('(code: -32000)') ) { // Clear auth cache since session is invalid // Note: We would need access to authStateManager for this, but for now we'll just show login prompt const result = await vscode.window.showWarningMessage( - 'Your session has expired. Please login again to continue using Qwen Code.', + 'Your login has expired. Please login again to continue using Qwen Code.', 'Login Now', ); if (result === 'Login Now') { - vscode.commands.executeCommand('qwenCode.login'); + if (this.loginHandler) { + await this.loginHandler(); + } else { + await vscode.commands.executeCommand('qwenCode.login'); + } } } else { vscode.window.showErrorMessage(`Error sending message: ${error}`); @@ -405,6 +411,23 @@ export class SessionMessageHandler extends BaseMessageHandler { try { console.log('[SessionMessageHandler] Creating new Qwen session...'); + // Ensure connection (login) before creating a new session + if (!this.agentManager.isConnected) { + const result = await vscode.window.showWarningMessage( + 'You need to login before creating a new session.', + 'Login Now', + ); + if (result === 'Login Now') { + if (this.loginHandler) { + await this.loginHandler(); + } else { + await vscode.commands.executeCommand('qwenCode.login'); + } + } else { + return; + } + } + // Save current session before creating new one if (this.currentConversationId && this.agentManager.isConnected) { try { @@ -450,6 +473,39 @@ export class SessionMessageHandler extends BaseMessageHandler { try { console.log('[SessionMessageHandler] Switching to session:', sessionId); + // If not connected yet, offer to login or view offline + if (!this.agentManager.isConnected) { + const selection = await vscode.window.showWarningMessage( + 'You are not logged in. Login now to fully restore this session, or view it offline.', + 'Login Now', + 'View Offline', + ); + + if (selection === 'Login Now') { + if (this.loginHandler) { + await this.loginHandler(); + } else { + await vscode.commands.executeCommand('qwenCode.login'); + } + } else if (selection === 'View Offline') { + // Show messages from local cache only + const messages = + await this.agentManager.getSessionMessages(sessionId); + this.currentConversationId = sessionId; + this.sendToWebView({ + type: 'qwenSessionSwitched', + data: { sessionId, messages }, + }); + vscode.window.showInformationMessage( + 'Showing cached session content. Login to interact with the AI.', + ); + return; + } else { + // User dismissed; do nothing + return; + } + } + // Save current session before switching if ( this.currentConversationId && @@ -489,7 +545,7 @@ export class SessionMessageHandler extends BaseMessageHandler { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; const workingDir = workspaceFolder?.uri.fsPath || process.cwd(); - // Try to load session via ACP + // Try to load session via ACP (now we should be connected) try { const loadResponse = await this.agentManager.loadSessionViaAcp(sessionId); @@ -514,26 +570,39 @@ export class SessionMessageHandler extends BaseMessageHandler { // Fallback: create new session const messages = await this.agentManager.getSessionMessages(sessionId); - try { - const newAcpSessionId = - await this.agentManager.createNewSession(workingDir); + // If we are connected, try to create a fresh ACP session so user can interact + if (this.agentManager.isConnected) { + try { + const newAcpSessionId = + await this.agentManager.createNewSession(workingDir); - this.currentConversationId = newAcpSessionId; + this.currentConversationId = newAcpSessionId; + this.sendToWebView({ + type: 'qwenSessionSwitched', + data: { sessionId, messages, session: sessionDetails }, + }); + + vscode.window.showWarningMessage( + 'Session restored from local cache. Some context may be incomplete.', + ); + } catch (createError) { + console.error( + '[SessionMessageHandler] Failed to create session:', + createError, + ); + throw createError; + } + } else { + // Offline view only + this.currentConversationId = sessionId; this.sendToWebView({ type: 'qwenSessionSwitched', data: { sessionId, messages, session: sessionDetails }, }); - vscode.window.showWarningMessage( - 'Session restored from local cache. Some context may be incomplete.', + 'Showing cached session content. Login to interact with the AI.', ); - } catch (createError) { - console.error( - '[SessionMessageHandler] Failed to create session:', - createError, - ); - throw createError; } } } catch (error) { @@ -620,6 +689,37 @@ export class SessionMessageHandler extends BaseMessageHandler { */ private async handleResumeSession(sessionId: string): Promise { try { + // If not connected, offer to login or view offline + if (!this.agentManager.isConnected) { + const selection = await vscode.window.showWarningMessage( + 'You are not logged in. Login now to fully restore this session, or view it offline.', + 'Login Now', + 'View Offline', + ); + + if (selection === 'Login Now') { + if (this.loginHandler) { + await this.loginHandler(); + } else { + await vscode.commands.executeCommand('qwenCode.login'); + } + } else if (selection === 'View Offline') { + const messages = + await this.agentManager.getSessionMessages(sessionId); + this.currentConversationId = sessionId; + this.sendToWebView({ + type: 'qwenSessionSwitched', + data: { sessionId, messages }, + }); + vscode.window.showInformationMessage( + 'Showing cached session content. Login to interact with the AI.', + ); + return; + } else { + return; + } + } + // Try ACP load first try { await this.agentManager.loadSessionViaAcp(sessionId);