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
+1241 -2535
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -5,4 +5,4 @@
[ ] Change Waiting for AI messages..." to "Waiting for AI MCP Tool Call...
[ ] Get it to store the Friendly Name similar to how it stores the session ID - the existing session seems to be found on startup
[ ] Override Prompt Script is not generated from exact coded system tool description - seems to be stale - where is the info coming from for the override creation?
[ ]
[ ] Can we include an option to enable logging and set logging to take place in the .vscode directory of the active workspace. It should be an option that the user can turn on in the cog.
+5
View File
@@ -26,3 +26,8 @@
2025-10-24T02:33:40.664Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761273090722
2025-10-24T02:34:46.816Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761273266913
2025-10-24T02:39:08.715Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761273289660
2025-10-24T02:46:25.959Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761273975576
2025-10-24T02:46:34.992Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761273990658
2025-10-24T02:46:37.893Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761273997564
2025-10-24T02:51:07.733Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761274000828
2025-10-24T02:51:36.761Z - RESPONSE ENDPOINT CALLED - RequestID: 7-1761274270525
+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}`);