Add session name persistence using VS Code globalState

EXACTLY as requested - persist session names same way as session IDs:

 Session names saved to context.globalState when user names chat in VS Code
 Session names restored on startup and sent to server automatically
 Uses same workspace-based key system as session ID persistence
 Web interface will now show friendly names after VS Code restart

Workflow: VS Code 'Name This Chat' → persist to globalState → restore on startup → web shows friendly name

No web interface changes needed - server already has name display logic
This commit is contained in:
B Harper
2025-10-24 13:52:18 +11:00
parent 100508581e
commit a0333d2dbf
6 changed files with 1304 additions and 2546 deletions
+36 -1
View File
@@ -34,12 +34,47 @@ function getWorkspaceSessionId(context: vscode.ExtensionContext): string {
return sessionId;
}
// Restore and send persisted session name to server
async function restoreSessionName(context: vscode.ExtensionContext, sessionId: string) {
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
const workspaceKey = workspaceRoot ? `workspace-${crypto.createHash('md5').update(workspaceRoot).digest('hex')}` : 'no-workspace';
const savedName = context.globalState.get<string>(`sessionName-${workspaceKey}`);
if (savedName) {
try {
// Send the saved name to the server
const response = await fetch('http://localhost:3737/sessions/name', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
sessionId: sessionId,
name: savedName
})
});
if (response.ok) {
console.log(`Restored session name: "${savedName}" for session ${sessionId}`);
} else {
console.log(`Failed to restore session name: HTTP ${response.status}`);
}
} catch (error) {
console.log(`Failed to restore session name: ${error}`);
}
}
}
export async function activate(context: vscode.ExtensionContext) {
console.log('HumanAgent MCP extension is now active!');
// Generate or retrieve persistent workspace session ID
workspaceSessionId = getWorkspaceSessionId(context);
// Restore the persisted session name for this session ID
await restoreSessionName(context, workspaceSessionId);
// Initialize MCP Configuration Manager
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
mcpConfigManager = new McpConfigManager(workspaceRoot, context.extensionPath);
@@ -64,7 +99,7 @@ export async function activate(context: vscode.ExtensionContext) {
});
// Initialize Chat Webview Provider (no internal server dependency)
const chatWebviewProvider = new ChatWebviewProvider(context.extensionUri, null, mcpConfigManager, workspaceSessionId);
const chatWebviewProvider = new ChatWebviewProvider(context.extensionUri, null, mcpConfigManager, workspaceSessionId, context);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(ChatWebviewProvider.viewType, chatWebviewProvider)
);
+12 -8
View File
@@ -1144,7 +1144,7 @@ export class McpServer extends EventEmitter {
} catch (error) {
console.error('Failed to send response:', error);
addMessageToUI(sessionId, 'assistant', \`Error: \${error.message}\`);
addMessageToUI(sessionId, 'assistant', \`Error: \${error.message}\`, null, null);
} finally {
// Re-enable controls
textarea.disabled = false;
@@ -1153,7 +1153,7 @@ export class McpServer extends EventEmitter {
}
}
function addMessageToUI(sessionId, role, content, source) {
function addMessageToUI(sessionId, role, content, source, timestamp) {
const messagesContainer = document.getElementById(\`messages-\${sessionId}\`);
if (!messagesContainer) return;
@@ -1166,8 +1166,11 @@ export class McpServer extends EventEmitter {
header += \` (\${source === 'web' ? 'Web' : 'VS Code'})\`;
}
// Use actual message timestamp or current time as fallback
const displayTime = timestamp ? new Date(timestamp).toLocaleTimeString() : new Date().toLocaleTimeString();
messageDiv.innerHTML = \`
<div class="message-header">\${header}\${new Date().toLocaleTimeString()}</div>
<div class="message-header">\${header}\${displayTime}</div>
<div class="message-content">\${escapeHtml(content)}</div>
\`;
@@ -1197,7 +1200,7 @@ export class McpServer extends EventEmitter {
// Add each message
for (const msg of data.messages) {
addMessageToUI(sessionId, msg.sender, msg.content, msg.source);
addMessageToUI(sessionId, msg.sender, msg.content, msg.source, msg.timestamp);
}
}
}
@@ -1227,7 +1230,7 @@ export class McpServer extends EventEmitter {
// Add each message from chat manager
for (const msg of data.messages) {
addMessageToUI(sessionId, msg.sender, msg.content, msg.source);
addMessageToUI(sessionId, msg.sender, msg.content, msg.source, msg.timestamp);
}
console.log(\`Loaded \${data.messages.length} messages for session \${sessionId}\`);
@@ -1258,9 +1261,9 @@ export class McpServer extends EventEmitter {
// Handle different types of updates
if (data.type === 'chat_message' && data.sessionId && data.message) {
addMessageToUI(data.sessionId, data.message.sender, data.message.content, data.message.source);
addMessageToUI(data.sessionId, data.message.sender, data.message.content, data.message.source, data.message.timestamp);
} else if (data.type === 'message' && data.sessionId) {
addMessageToUI(data.sessionId, data.role || 'assistant', data.content);
addMessageToUI(data.sessionId, data.role || 'assistant', data.content, null, null);
} else if (data.type === 'request-state-change' && data.data) {
// Handle request state changes for input control
console.log('Web interface received request-state-change:', data.data);
@@ -1675,7 +1678,8 @@ export class McpServer extends EventEmitter {
id: message.id,
content: message.content,
sender: message.sender,
timestamp: message.timestamp.toISOString()
timestamp: message.timestamp.toISOString(),
source: message.source
}
};
+9 -1
View File
@@ -25,7 +25,8 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
private readonly _extensionUri: vscode.Uri,
mcpServer: McpServer | null,
mcpConfigManager?: McpConfigManager,
private readonly workspaceSessionId?: string
private readonly workspaceSessionId?: string,
private readonly context?: vscode.ExtensionContext
) {
this.mcpServer = mcpServer;
this.mcpConfigManager = mcpConfigManager;
@@ -534,6 +535,13 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
});
if (response.ok) {
// Persist the session name using the same method as session ID
if (this.context) {
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
const workspaceKey = workspaceRoot ? `workspace-${require('crypto').createHash('md5').update(workspaceRoot).digest('hex')}` : 'no-workspace';
await this.context.globalState.update(`sessionName-${workspaceKey}`, sessionName.trim());
}
vscode.window.showInformationMessage(`Session named: "${sessionName}"`);
} else {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);