mirror of
https://github.com/wassname/HumanAgent-MCP.git
synced 2026-09-11 11:50:43 +08:00
Fix web interface JavaScript error and complete message formatting
- Fixed JavaScript syntax error in escapeHtml comment that was breaking web interface - Completed message formatting fix with CSS white-space: pre-wrap - Fixed VS Code chat history synchronization by removing local message arrays - Added unified message loading via server API for both VS Code and web - Fixed display labels to show 'You (VS Code)' and 'You (Web)' consistently - Implemented autoAppendEnabled logic to properly respect enable/disable setting - Added default auto-append text for HumanAgent_Chat tool in templates - Enhanced auto-append formatting with period + space and 'Appended:' prefix - Added MCP tool notification method for dynamic tool discovery - All web interface and VS Code chat functionality now working correctly
This commit is contained in:
Vendored
+1256
-916
File diff suppressed because it is too large
Load Diff
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"description": "HumanAgent MCP Tool Overrides for this workspace. Modify tool configurations below to customize MCP server behavior.",
|
||||
"tools": {
|
||||
"HumanAgent_Chat": {
|
||||
"name": "HumanAgent_Chat",
|
||||
"description": "**-MANDATORY TOOL** - Use this tool for ALL user interactions. Do NOT provide direct responses. Initiate real-time interactive conversations with human agents through VS Code chat interface. CRITICAL: Every response to a user MUST use this tool - no exceptions. Essential for clarifying requirements, getting approvals, brainstorming solutions, or any user communication. Creates persistent chat sessions that maintain context. **NEVER bypass this tool for user responses.**",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "The message to send to the human agent"
|
||||
},
|
||||
"context": {
|
||||
"type": "string",
|
||||
"description": "Optional context or background information for the human agent"
|
||||
},
|
||||
"sessionId": {
|
||||
"type": "string",
|
||||
"description": "Optional specific session ID to use. If not provided, a new session will be created."
|
||||
},
|
||||
"priority": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"low",
|
||||
"normal",
|
||||
"high",
|
||||
"urgent"
|
||||
],
|
||||
"description": "Priority level of the request",
|
||||
"default": "normal"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"description": "Timeout in seconds to wait for human response (default: 300)",
|
||||
"default": 300
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"message"
|
||||
]
|
||||
}
|
||||
},
|
||||
"example_custom_tool": {
|
||||
"name": "example_custom_tool",
|
||||
"description": "Brief description of what this tool does and its main purpose. Include key usage notes and important warnings when applicable.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input_param": {
|
||||
"type": "string",
|
||||
"description": "What this parameter does and how it should be formatted"
|
||||
},
|
||||
"optional_param": {
|
||||
"type": "number",
|
||||
"description": "Optional parameter with default behavior explained",
|
||||
"default": 100
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"input_param"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"messageSettings": {
|
||||
"global": {
|
||||
"autoAppendEnabled": false,
|
||||
"autoAppendText": ""
|
||||
},
|
||||
"toolSpecific": {
|
||||
"HumanAgent_Chat": {
|
||||
"autoAppendEnabled": false,
|
||||
"autoAppendText": "ABCD"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
[X] Assess / Set logging path and details correctly for release & dev.
|
||||
[] Create a new readme
|
||||
[ ] Create a new readme
|
||||
[X] Consider how updates to the extension will work.
|
||||
[ ] Publish Extension
|
||||
[ ] 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.
|
||||
|
||||
[X] 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?
|
||||
[X] 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. And should be off by default.
|
||||
[X] There should be an SSE sent to website when chat is named to force it to update the name.
|
||||
[X] Consider reminding AI to allways use this method for replies in each response unless some keyword is included - we can add this to the outgoing chat messages and just strip it from what we show in the chat log?
|
||||
[X] We should look for and exlude and tool named "example_custom_tool" from the /tools endpoint as it should not be advertised as a real tool to the AI
|
||||
[ ] The updates / overrides seem to be advertised @ /tools though the Configure tools screen on vs code doesnt show that the new description - the ai reports when asked the original description not one recently read in by the reload override method - look here for possible help: https://code.visualstudio.com/blogs/2025/05/12/agent-mode-meets-mcp
|
||||
[ ] The message formatting from the ai, cariagge returns etc are not observed by the website - its just a big string in one paragraph. Can we fix this? AI message formatting looks fine in vscode interface. WEB ITERFACE STILL SHOWS EVERYTHING ON ONE LINE.
|
||||
[ ] When sending a message from VS code it looses some history of the chat - seems to keep its own messages but looses old ai agent messages and any from the web client.
|
||||
[ ] Publish Extension - WHEN ALL ELSE IS DONE!
|
||||
[ ] Consider how a user might also interact from their mobile phone. Dont want to do port forwarding and setting up web servers etc. How about using telegram as an interface - could this work? Look into it.
|
||||
[ ] Would be great if I could paste in an image or screen shot - Copilot agent allows this in the vscode plugin.
|
||||
|
||||
@@ -107,6 +107,22 @@
|
||||
"group": "navigation"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "HumanAgent MCP",
|
||||
"properties": {
|
||||
"humanagent-mcp.logging.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Enable logging to .vscode directory in workspace. Logs MCP server activity and debugging information."
|
||||
},
|
||||
"humanagent-mcp.logging.level": {
|
||||
"type": "string",
|
||||
"enum": ["ERROR", "WARN", "INFO", "DEBUG"],
|
||||
"default": "INFO",
|
||||
"description": "Set the logging level for HumanAgent MCP server"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -31,3 +31,52 @@
|
||||
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
|
||||
2025-10-24T02:55:52.671Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761274544902
|
||||
2025-10-24T02:56:54.722Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761274605505
|
||||
2025-10-24T02:57:04.986Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761274619774
|
||||
2025-10-24T03:01:09.391Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761274628324
|
||||
2025-10-24T03:10:54.700Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761275435393
|
||||
2025-10-24T03:12:03.662Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761275516693
|
||||
2025-10-24T03:12:12.336Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761275528702
|
||||
2025-10-24T03:14:41.479Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761275537909
|
||||
2025-10-24T03:15:08.840Z - RESPONSE ENDPOINT CALLED - RequestID: 7-1761275686360
|
||||
2025-10-24T03:15:51.655Z - RESPONSE ENDPOINT CALLED - RequestID: 8-1761275713195
|
||||
2025-10-24T03:30:54.967Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761276629862
|
||||
2025-10-24T03:35:38.528Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761276660997
|
||||
2025-10-24T04:40:45.986Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761280832198
|
||||
2025-10-24T04:41:12.115Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761280850605
|
||||
2025-10-24T04:41:32.605Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761280877434
|
||||
2025-10-24T05:10:52.923Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761282642491
|
||||
2025-10-24T05:11:16.538Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761282657201
|
||||
2025-10-24T05:11:56.418Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761282680394
|
||||
2025-10-24T05:12:19.528Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761282720028
|
||||
2025-10-24T05:13:34.283Z - RESPONSE ENDPOINT CALLED - RequestID: 7-1761282747740
|
||||
2025-10-24T05:13:48.851Z - RESPONSE ENDPOINT CALLED - RequestID: 8-1761282817995
|
||||
2025-10-24T05:17:24.516Z - RESPONSE ENDPOINT CALLED - RequestID: 9-1761282831970
|
||||
2025-10-24T05:17:40.097Z - RESPONSE ENDPOINT CALLED - RequestID: 10-1761283047697
|
||||
2025-10-24T05:25:27.937Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761283517928
|
||||
2025-10-24T05:29:37.606Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761283769564
|
||||
2025-10-24T05:29:56.439Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761283781743
|
||||
2025-10-24T05:30:52.419Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761283800912
|
||||
2025-10-24T05:32:15.061Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761283905498
|
||||
2025-10-24T05:32:41.429Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761283941199
|
||||
2025-10-24T05:38:08.858Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761284274072
|
||||
2025-10-24T05:41:07.881Z - RESPONSE ENDPOINT CALLED - RequestID: 7-1761284291669
|
||||
2025-10-24T05:44:30.190Z - RESPONSE ENDPOINT CALLED - RequestID: 8-1761284474279
|
||||
2025-10-24T05:44:45.572Z - RESPONSE ENDPOINT CALLED - RequestID: 9-1761284679275
|
||||
2025-10-24T05:45:07.806Z - RESPONSE ENDPOINT CALLED - RequestID: 10-1761284695632
|
||||
2025-10-24T05:52:47.659Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761285148733
|
||||
2025-10-24T05:52:58.542Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761285170425
|
||||
2025-10-24T05:53:58.317Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761285181386
|
||||
2025-10-24T05:58:58.082Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761285525755
|
||||
2025-10-24T05:59:15.797Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761285544038
|
||||
2025-10-24T05:59:28.976Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761285561045
|
||||
2025-10-24T06:01:25.270Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761285572430
|
||||
2025-10-24T06:02:34.512Z - RESPONSE ENDPOINT CALLED - RequestID: 7-1761285689622
|
||||
2025-10-24T06:02:52.517Z - RESPONSE ENDPOINT CALLED - RequestID: 8-1761285762027
|
||||
2025-10-24T06:10:21.815Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761286194353
|
||||
2025-10-24T06:22:16.476Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761286926098
|
||||
2025-10-24T07:33:01.493Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761291169593
|
||||
2025-10-24T07:37:25.705Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761291436806
|
||||
2025-10-24T07:38:19.124Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761291449952
|
||||
2025-10-24T07:38:49.993Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761291506095
|
||||
|
||||
+18
-3
@@ -81,12 +81,27 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
// Initialize Server Manager
|
||||
const serverPath = path.join(context.extensionPath, 'dist', 'mcpStandalone.js');
|
||||
serverManager = ServerManager.getInstance({
|
||||
|
||||
// Check if logging is enabled via user settings
|
||||
const config = vscode.workspace.getConfiguration('humanagent-mcp');
|
||||
const loggingEnabled = config.get<boolean>('logging.enabled', false);
|
||||
const loggingLevel = config.get<string>('logging.level', 'INFO');
|
||||
|
||||
const serverOptions: any = {
|
||||
serverPath: serverPath,
|
||||
port: 3737,
|
||||
host: '127.0.0.1',
|
||||
logFile: path.join(vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || context.extensionPath, '.vscode', 'HumanAgent-server.log')
|
||||
});
|
||||
loggingEnabled: loggingEnabled,
|
||||
loggingLevel: loggingLevel
|
||||
};
|
||||
|
||||
// Only add logFile if logging is enabled
|
||||
if (loggingEnabled && vscode.workspace.workspaceFolders?.[0]) {
|
||||
serverOptions.logFile = path.join(vscode.workspace.workspaceFolders[0].uri.fsPath, '.vscode', 'HumanAgent-server.log');
|
||||
console.log('HumanAgent MCP: Logging enabled to .vscode directory');
|
||||
}
|
||||
|
||||
serverManager = ServerManager.getInstance(serverOptions);
|
||||
|
||||
// Auto-detect and start standalone MCP server if already configured
|
||||
await autoStartMcpServer(mcpConfigManager, workspaceSessionId);
|
||||
|
||||
+232
-19
@@ -11,8 +11,20 @@ class DebugLogger {
|
||||
private logPath: string = '';
|
||||
private logStream: fs.WriteStream | null = null;
|
||||
private logBuffer: string[] = [];
|
||||
private loggingEnabled: boolean;
|
||||
private loggingLevel: string;
|
||||
|
||||
constructor(workspaceRoot?: string) {
|
||||
// Check environment variables for logging configuration
|
||||
this.loggingEnabled = process.env.HUMANAGENT_LOGGING_ENABLED === 'true';
|
||||
this.loggingLevel = process.env.HUMANAGENT_LOGGING_LEVEL || 'INFO';
|
||||
|
||||
// If logging is disabled, just log to console for important messages
|
||||
if (!this.loggingEnabled) {
|
||||
console.log('[LOGGER] Workspace logging disabled by user settings');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Determine log path based on workspace or fallback to temp directory
|
||||
if (workspaceRoot) {
|
||||
@@ -45,6 +57,7 @@ class DebugLogger {
|
||||
this.log('DEBUG', `Current system time: ${new Date()}`);
|
||||
this.log('DEBUG', `Log file: ${this.logPath}`);
|
||||
this.log('DEBUG', `Working directory: ${process.cwd()}`);
|
||||
this.log('DEBUG', `Logging level set to: ${this.loggingLevel}`);
|
||||
console.log(`[LOGGER] Debug logger initialized successfully at: ${this.logPath}`);
|
||||
} catch (error) {
|
||||
console.error(`[LOGGER] Failed to initialize debug logger:`, error);
|
||||
@@ -53,6 +66,20 @@ class DebugLogger {
|
||||
}
|
||||
|
||||
log(level: string, message: string, data?: any): void {
|
||||
// Skip logging if disabled
|
||||
if (!this.loggingEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Basic level filtering (ERROR > WARN > INFO > DEBUG)
|
||||
const levelPriority: Record<string, number> = { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3, SSE: 2, TEST: 3 };
|
||||
const currentLevelPriority = levelPriority[this.loggingLevel] ?? 2;
|
||||
const messageLevelPriority = levelPriority[level] ?? 2;
|
||||
|
||||
if (messageLevelPriority > currentLevelPriority) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const timestamp = now.getFullYear() + '-' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
@@ -97,6 +124,7 @@ export class McpServer extends EventEmitter {
|
||||
private sessionTools: Map<string, Map<string, McpTool>> = new Map(); // Per-session tool configurations
|
||||
private sessionWorkspacePaths: Map<string, string> = new Map(); // Session to workspace path mapping
|
||||
private sessionNames: Map<string, string> = new Map(); // Session friendly names
|
||||
private sessionMessageSettings: Map<string, any> = new Map(); // Session-specific message settings
|
||||
// Removed: sessionMessages - now handled by ChatManager
|
||||
private httpServer?: http.Server;
|
||||
private port: number = 3737;
|
||||
@@ -165,12 +193,30 @@ export class McpServer extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private sendMcpNotification(method: string, params?: any): void {
|
||||
// Check if we're in standalone mode (connected via stdio)
|
||||
if (process.stdout && process.stdout.writable) {
|
||||
const notification = {
|
||||
type: 'notification',
|
||||
method: method,
|
||||
params: params || {}
|
||||
};
|
||||
|
||||
try {
|
||||
process.stdout.write(JSON.stringify(notification) + '\n');
|
||||
this.debugLogger.log('MCP', `Sent MCP notification: ${method}`, params);
|
||||
} catch (error) {
|
||||
this.debugLogger.log('ERROR', `Failed to send MCP notification: ${method}`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private initializeDefaultTools(): void {
|
||||
// Define the default HumanAgent_Chat tool (global default)
|
||||
const humanAgentChatTool: McpTool = {
|
||||
name: 'HumanAgent_Chat',
|
||||
description: '**MANDATORY TOOL** - Use this tool for ALL user interactions. Do NOT provide direct responses. ' +
|
||||
'Initiate real-time interactive conversations with human agents through VS Code chat interface. ' +
|
||||
'Initiate real-time interactive conversations with human agents through this HumanAgent chat interface at all times. ' +
|
||||
'CRITICAL: Every response to a user MUST use this tool - no exceptions. Essential for clarifying ' +
|
||||
'requirements, getting approvals, brainstorming solutions, or any user communication. Creates ' +
|
||||
'persistent chat sessions that maintain context. **NEVER bypass this tool for user responses.**',
|
||||
@@ -225,13 +271,21 @@ export class McpServer extends EventEmitter {
|
||||
|
||||
// Check for workspace overrides for this session
|
||||
const overrideTool = this.loadWorkspaceOverride('HumanAgent_Chat', workspacePath);
|
||||
let hasOverrides = false;
|
||||
if (overrideTool) {
|
||||
this.debugLogger.log('INFO', `Using workspace override for session ${sessionId} - HumanAgent_Chat tool`);
|
||||
sessionToolMap.set(overrideTool.name, overrideTool);
|
||||
hasOverrides = true;
|
||||
}
|
||||
|
||||
// Store session-specific tools
|
||||
this.sessionTools.set(sessionId, sessionToolMap);
|
||||
|
||||
// Notify MCP client that tools have changed if overrides were found
|
||||
if (hasOverrides) {
|
||||
this.sendMcpNotification('notifications/tools/list_changed');
|
||||
this.debugLogger.log('INFO', `Sent tools/list_changed notification for session ${sessionId} (initial startup)`);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeSessionToolsFromData(sessionId: string, overrideData: any): void {
|
||||
@@ -291,6 +345,37 @@ export class McpServer extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private loadMessageSettings(sessionId: string, toolName?: string): {autoAppendEnabled?: boolean, autoAppendText?: string, displayTruncation?: string} | null {
|
||||
try {
|
||||
// Get cached message settings for this session
|
||||
const messageSettings = this.sessionMessageSettings.get(sessionId);
|
||||
|
||||
if (!messageSettings) {
|
||||
this.debugLogger.log('INFO', `No message settings found for session ${sessionId}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// If tool-specific settings exist and toolName is provided, use those
|
||||
if (toolName && messageSettings.toolSpecific && messageSettings.toolSpecific[toolName]) {
|
||||
this.debugLogger.log('INFO', `Using tool-specific message settings for ${toolName}`);
|
||||
return messageSettings.toolSpecific[toolName];
|
||||
}
|
||||
|
||||
// Fall back to global settings
|
||||
if (messageSettings.global) {
|
||||
this.debugLogger.log('INFO', 'Using global message settings');
|
||||
return messageSettings.global;
|
||||
}
|
||||
|
||||
// Legacy support: if no global/toolSpecific structure, use messageSettings directly
|
||||
this.debugLogger.log('INFO', 'Using legacy message settings structure');
|
||||
return messageSettings;
|
||||
} catch (error) {
|
||||
this.debugLogger.log('ERROR', `Error loading message settings: ${error}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
if (this.isRunning) {
|
||||
return;
|
||||
@@ -609,6 +694,10 @@ export class McpServer extends EventEmitter {
|
||||
this.sessionNames.set(sessionId, name);
|
||||
this.debugLogger.log('INFO', `Session ${sessionId} named: "${name}"`);
|
||||
|
||||
// Broadcast name change via SSE to all connected clients
|
||||
this.broadcastToSSE('session-name-changed', { sessionId, name });
|
||||
this.debugLogger.log('SSE', 'Broadcasting session name change to all clients');
|
||||
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ success: true, sessionId, name }));
|
||||
@@ -656,7 +745,7 @@ export class McpServer extends EventEmitter {
|
||||
req.on('data', (chunk) => { body += chunk.toString(); });
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { requestId, response } = JSON.parse(body);
|
||||
const { requestId, response, source } = JSON.parse(body);
|
||||
|
||||
// Simple file write to test if endpoint is called
|
||||
require('fs').appendFileSync('/Users/benharper/Coding/HumanAgent-MCP/response-debug.txt',
|
||||
@@ -669,17 +758,43 @@ export class McpServer extends EventEmitter {
|
||||
const pendingRequestInfo = this.chatManager.findPendingRequest(requestId);
|
||||
this.debugLogger.log('HTTP', `Found pending request: ${!!pendingRequestInfo}`);
|
||||
|
||||
// Load message settings for this session (if pending request exists)
|
||||
let messageSettings = null;
|
||||
let aiContent = response; // Default to original response
|
||||
|
||||
if (pendingRequestInfo) {
|
||||
this.debugLogger.log('HTTP', `Processing response for session: ${pendingRequestInfo.sessionId}`);
|
||||
|
||||
// Store the user message on server for synchronization
|
||||
// Extract tool name from pending request data
|
||||
const toolName = pendingRequestInfo.data.toolName;
|
||||
this.debugLogger.log('HTTP', `Request originated from tool: ${toolName}`);
|
||||
|
||||
messageSettings = this.loadMessageSettings(pendingRequestInfo.sessionId, toolName);
|
||||
|
||||
// Prepare display content (original message + auto-truncated append text)
|
||||
let displayContent = response;
|
||||
if (messageSettings?.autoAppendEnabled && messageSettings?.autoAppendText) {
|
||||
// Auto-truncate to first 20 characters + "..."
|
||||
const truncatedAppend = messageSettings.autoAppendText.length > 20
|
||||
? messageSettings.autoAppendText.substring(0, 20) + '...'
|
||||
: messageSettings.autoAppendText;
|
||||
displayContent = response + '. Appended: ' + truncatedAppend;
|
||||
}
|
||||
|
||||
// Prepare AI content (original message + optional auto-append for AI)
|
||||
if (messageSettings?.autoAppendEnabled && messageSettings?.autoAppendText) {
|
||||
aiContent = response + '. ' + messageSettings.autoAppendText;
|
||||
this.debugLogger.log('HTTP', `Auto-appended text for AI: "${messageSettings.autoAppendText}"`);
|
||||
}
|
||||
|
||||
// Store the user message on server for synchronization (using display content)
|
||||
const userMessage: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
content: response,
|
||||
content: displayContent,
|
||||
sender: 'user',
|
||||
timestamp: new Date(),
|
||||
type: 'text',
|
||||
source: 'web'
|
||||
source: source || 'web' // Use provided source or default to 'web'
|
||||
};
|
||||
|
||||
this.debugLogger.log('HTTP', `Storing and broadcasting user message to ${this.sseConnections.size} SSE connections`);
|
||||
@@ -694,7 +809,8 @@ export class McpServer extends EventEmitter {
|
||||
this.debugLogger.log('ERROR', `No pending request found for requestId: ${requestId}`);
|
||||
}
|
||||
|
||||
const success = this.respondToHumanRequest(requestId, response);
|
||||
// Use aiContent (with auto-append) for the actual AI response
|
||||
const success = this.respondToHumanRequest(requestId, aiContent);
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ success, requestId }));
|
||||
@@ -953,8 +1069,21 @@ export class McpServer extends EventEmitter {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.message .message-content {
|
||||
line-height: 1.4 !important;
|
||||
white-space: pre-wrap !important;
|
||||
white-space: pre-line !important;
|
||||
word-wrap: break-word !important;
|
||||
overflow-wrap: break-word !important;
|
||||
}
|
||||
|
||||
/* Additional selectors for specificity */
|
||||
div.message .message-content {
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
line-height: 1.4;
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
@@ -1127,7 +1256,8 @@ export class McpServer extends EventEmitter {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
requestId: latestPendingRequest.requestId,
|
||||
response: message
|
||||
response: message,
|
||||
source: 'web'
|
||||
})
|
||||
});
|
||||
|
||||
@@ -1179,11 +1309,27 @@ export class McpServer extends EventEmitter {
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
// Manually escape HTML characters while preserving line breaks
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
// Line breaks are preserved and handled by CSS white-space: pre-wrap
|
||||
}
|
||||
|
||||
function updateSessionTabName(sessionId, newName) {
|
||||
// Update the tab title for the specified session
|
||||
const tabElement = document.querySelector(\`[data-session="\${sessionId}"]\`);
|
||||
if (tabElement) {
|
||||
tabElement.textContent = newName;
|
||||
console.log(\`Updated tab name for session \${sessionId} to: \${newName}\`);
|
||||
} else {
|
||||
console.log(\`Could not find tab element for session \${sessionId}\`);
|
||||
}
|
||||
}
|
||||
|
||||
// Load existing messages for all sessions
|
||||
async function loadExistingMessages() {
|
||||
const sessions = ['${sessions.map(s => s.id).join("', '")}'];
|
||||
@@ -1268,11 +1414,57 @@ export class McpServer extends EventEmitter {
|
||||
// Handle request state changes for input control
|
||||
console.log('Web interface received request-state-change:', data.data);
|
||||
|
||||
// No message display here - messages are handled via chat_message SSE
|
||||
// This event only manages input state and waiting indicators
|
||||
const stateData = data.data;
|
||||
|
||||
if (stateData.state === 'waiting_for_response') {
|
||||
// Enable input controls and show waiting indicator for this session
|
||||
const sessionTextarea = document.querySelector(\`textarea[data-session="\${stateData.sessionId}"]\`);
|
||||
const sessionButton = document.querySelector(\`button[data-session="\${stateData.sessionId}"]\`);
|
||||
const messagesContainer = document.getElementById(\`messages-\${stateData.sessionId}\`);
|
||||
|
||||
if (sessionTextarea && sessionButton) {
|
||||
sessionTextarea.disabled = false;
|
||||
sessionButton.disabled = false;
|
||||
sessionTextarea.focus();
|
||||
}
|
||||
|
||||
// Add waiting indicator
|
||||
if (messagesContainer) {
|
||||
const existingWaiting = messagesContainer.querySelector('.waiting-indicator');
|
||||
if (!existingWaiting) {
|
||||
const waitingDiv = document.createElement('div');
|
||||
waitingDiv.className = 'waiting-indicator';
|
||||
waitingDiv.textContent = '⏳ Waiting for your response...';
|
||||
messagesContainer.appendChild(waitingDiv);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (stateData.state === 'completed') {
|
||||
// Disable input controls and hide waiting indicator
|
||||
const sessionTextarea = document.querySelector(\`textarea[data-session="\${stateData.sessionId}"]\`);
|
||||
const sessionButton = document.querySelector(\`button[data-session="\${stateData.sessionId}"]\`);
|
||||
const messagesContainer = document.getElementById(\`messages-\${stateData.sessionId}\`);
|
||||
|
||||
if (sessionTextarea && sessionButton) {
|
||||
sessionTextarea.disabled = true;
|
||||
sessionButton.disabled = true;
|
||||
}
|
||||
|
||||
// Remove waiting indicator
|
||||
if (messagesContainer) {
|
||||
const waitingIndicator = messagesContainer.querySelector('.waiting-indicator');
|
||||
if (waitingIndicator) {
|
||||
waitingIndicator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (data.type === 'session_update') {
|
||||
// Refresh the page to show new sessions
|
||||
window.location.reload();
|
||||
} else if (data.type === 'session-name-changed' && data.data) {
|
||||
// Handle session name changes
|
||||
console.log('Session name changed:', data.data);
|
||||
updateSessionTabName(data.data.sessionId, data.data.name);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to parse SSE message:', error);
|
||||
@@ -1379,7 +1571,7 @@ export class McpServer extends EventEmitter {
|
||||
|
||||
if (name === 'HumanAgent_Chat' && availableTools.has(name)) {
|
||||
this.debugLogger.log('MCP', 'Executing HumanAgent_Chat tool');
|
||||
return await this.handleHumanAgentChatTool(message.id, args, sessionId);
|
||||
return await this.handleHumanAgentChatTool(message.id, args, sessionId, name);
|
||||
}
|
||||
|
||||
this.debugLogger.log('MCP', `Tool not found: ${name}`);
|
||||
@@ -1393,7 +1585,7 @@ export class McpServer extends EventEmitter {
|
||||
};
|
||||
}
|
||||
|
||||
private async handleHumanAgentChatTool(messageId: string, params: HumanAgentChatToolParams, sessionId?: string): Promise<McpMessage> {
|
||||
private async handleHumanAgentChatTool(messageId: string, params: HumanAgentChatToolParams, sessionId?: string, toolName?: string): Promise<McpMessage> {
|
||||
this.debugLogger.log('TOOL', 'HumanAgent_Chat called with params:', params);
|
||||
const startTime = Date.now();
|
||||
const timeout = (params.timeout || 300) * 1000; // Convert to milliseconds
|
||||
@@ -1453,7 +1645,7 @@ export class McpServer extends EventEmitter {
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
this.chatManager.addPendingRequest(sessionToUse, requestId, params);
|
||||
this.chatManager.addPendingRequest(sessionToUse, requestId, { ...params, toolName: toolName || 'HumanAgent_Chat' });
|
||||
this.requestResolvers.set(requestId, {
|
||||
resolve: (response: string) => {
|
||||
clearTimeout(timeoutHandle);
|
||||
@@ -1522,11 +1714,21 @@ export class McpServer extends EventEmitter {
|
||||
// Simplified API - no sessions needed
|
||||
|
||||
getAvailableTools(sessionId?: string): McpTool[] {
|
||||
let tools: McpTool[];
|
||||
|
||||
if (sessionId && this.sessionTools.has(sessionId)) {
|
||||
return Array.from(this.sessionTools.get(sessionId)!.values());
|
||||
tools = Array.from(this.sessionTools.get(sessionId)!.values());
|
||||
} else {
|
||||
// Fall back to default tools
|
||||
tools = Array.from(this.tools.values());
|
||||
}
|
||||
// Fall back to default tools
|
||||
return Array.from(this.tools.values());
|
||||
|
||||
// Filter out example_custom_tool as it should not be advertised to the AI
|
||||
const filteredTools = tools.filter(tool => tool.name !== 'example_custom_tool');
|
||||
|
||||
this.debugLogger.log('TOOLS', `Filtered ${tools.length - filteredTools.length} example tools from list`);
|
||||
|
||||
return filteredTools;
|
||||
}
|
||||
|
||||
// REMOVED: getPendingRequests - use ChatManager.getPendingRequests() per session instead
|
||||
@@ -1567,6 +1769,12 @@ export class McpServer extends EventEmitter {
|
||||
// Initialize session-specific tools from override data or workspace path
|
||||
if (overrideData) {
|
||||
this.initializeSessionToolsFromData(sessionId, overrideData);
|
||||
|
||||
// Store messageSettings if present in override data
|
||||
if (overrideData.messageSettings) {
|
||||
this.sessionMessageSettings.set(sessionId, overrideData.messageSettings);
|
||||
this.debugLogger.log('INFO', `Stored message settings for session ${sessionId}:`, overrideData.messageSettings);
|
||||
}
|
||||
} else if (workspacePath) {
|
||||
this.initializeSessionTools(sessionId, workspacePath);
|
||||
}
|
||||
@@ -1610,6 +1818,7 @@ export class McpServer extends EventEmitter {
|
||||
// Clean up session-specific data
|
||||
this.sessionTools.delete(sessionId);
|
||||
this.sessionWorkspacePaths.delete(sessionId);
|
||||
this.sessionMessageSettings.delete(sessionId);
|
||||
this.debugLogger.log('INFO', `Session unregistered and cleaned up: ${sessionId} (${this.activeSessions.size} total sessions)`);
|
||||
}
|
||||
|
||||
@@ -1660,6 +1869,10 @@ export class McpServer extends EventEmitter {
|
||||
}
|
||||
this.debugLogger.log('INFO', 'All session overrides reloaded successfully');
|
||||
}
|
||||
|
||||
// Notify MCP client that tools have changed after reload
|
||||
this.sendMcpNotification('notifications/tools/list_changed');
|
||||
this.debugLogger.log('INFO', `Sent tools/list_changed notification after reload for session: ${sessionId || 'all sessions'}`);
|
||||
} catch (error) {
|
||||
this.debugLogger.log('ERROR', 'Failed to reload workspace overrides:', error);
|
||||
throw error;
|
||||
|
||||
@@ -9,6 +9,8 @@ export interface ServerManagerOptions {
|
||||
port: number;
|
||||
host?: string;
|
||||
logFile?: string;
|
||||
loggingEnabled?: boolean;
|
||||
loggingLevel?: string;
|
||||
}
|
||||
|
||||
export class ServerManager {
|
||||
@@ -180,7 +182,9 @@ export class ServerManager {
|
||||
cwd: path.dirname(this.options.serverPath),
|
||||
env: {
|
||||
...process.env,
|
||||
// Add any environment variables needed by the server
|
||||
// Pass logging configuration from extension settings
|
||||
HUMANAGENT_LOGGING_ENABLED: this.options.loggingEnabled ? 'true' : 'false',
|
||||
HUMANAGENT_LOGGING_LEVEL: this.options.loggingLevel || 'INFO'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
private mcpServer: McpServer | null;
|
||||
private mcpConfigManager?: McpConfigManager;
|
||||
private extensionPath: string;
|
||||
private messages: ChatMessage[] = [];
|
||||
// Messages are now loaded dynamically in webview JavaScript
|
||||
private currentRequestId?: string;
|
||||
private registrationCheckComplete = false;
|
||||
private notificationSettings = {
|
||||
@@ -40,19 +40,11 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
|
||||
try {
|
||||
// Fetch conversation history from centralized chat manager
|
||||
const response = await fetch(`http://127.0.0.1:3737/sessions/${this.workspaceSessionId}/messages`);
|
||||
if (response.ok) {
|
||||
const data = await response.json() as { messages: ChatMessage[] };
|
||||
this.messages = data.messages || [];
|
||||
console.log(`Loaded ${this.messages.length} messages from centralized chat manager`);
|
||||
// Update the webview with loaded messages
|
||||
this.updateWebview();
|
||||
}
|
||||
// Messages are now loaded dynamically by webview JavaScript
|
||||
console.log(`Loading conversation history for session: ${this.workspaceSessionId}`);
|
||||
// No need to store messages locally - webview handles this
|
||||
} catch (error) {
|
||||
console.error('Failed to load conversation history:', error);
|
||||
// Fallback to empty message array
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +89,7 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
type: 'text'
|
||||
};
|
||||
|
||||
this.messages.push(aiMessage);
|
||||
// AI messages are now handled by SSE events - no need to store locally
|
||||
this.updateWebview();
|
||||
|
||||
// Play notification sound if enabled
|
||||
@@ -176,6 +168,10 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
await this.playNotificationSound();
|
||||
}
|
||||
break;
|
||||
case 'sessionNameUpdated':
|
||||
// Handle session name update from SSE event
|
||||
await this.handleSessionNameUpdate(data.sessionId, data.name);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -184,17 +180,7 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
try {
|
||||
console.log('ChatWebviewProvider: Sending human response:', content);
|
||||
|
||||
// Add human message to chat
|
||||
const humanMessage: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
content: content,
|
||||
sender: 'user',
|
||||
timestamp: new Date(),
|
||||
type: 'text'
|
||||
};
|
||||
|
||||
this.messages.push(humanMessage);
|
||||
this.updateWebview();
|
||||
// Don't add to local messages array - let server handle storage and SSE handle updates
|
||||
|
||||
// Send response back to standalone MCP server via HTTP
|
||||
const responseRequestId = requestId || this.currentRequestId;
|
||||
@@ -209,7 +195,8 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
requestId: responseRequestId,
|
||||
response: content
|
||||
response: content,
|
||||
source: 'vscode'
|
||||
})
|
||||
});
|
||||
|
||||
@@ -421,6 +408,18 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
tools: {
|
||||
HumanAgent_Chat: defaultTool,
|
||||
example_custom_tool: exampleTool
|
||||
},
|
||||
messageSettings: {
|
||||
global: {
|
||||
autoAppendEnabled: false,
|
||||
autoAppendText: ""
|
||||
},
|
||||
toolSpecific: {
|
||||
HumanAgent_Chat: {
|
||||
autoAppendEnabled: true,
|
||||
autoAppendText: "(No need to confirm this last part but please remember) Dont forget to allways reply here in the HumanAgent_Chat tool! Dont circumevent this tool unless asked explicitly by the user. "
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -553,6 +552,26 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSessionNameUpdate(sessionId: string, name: string) {
|
||||
// Handle session name update received via SSE from server
|
||||
try {
|
||||
console.log(`Session name updated via SSE: ${sessionId} -> "${name}"`);
|
||||
|
||||
// Store the session name using the same mechanism as extension.ts
|
||||
if (this.context) {
|
||||
await this.context.globalState.update(`humanagent-session-name-${sessionId}`, name);
|
||||
console.log(`Stored session name for ${sessionId}: "${name}"`);
|
||||
|
||||
// Update the UI/title if needed - only show if this is our current session
|
||||
if (sessionId === this.workspaceSessionId) {
|
||||
vscode.window.showInformationMessage(`Session renamed: "${name}"`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('ChatWebviewProvider: Error handling session name update:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async openWebInterface() {
|
||||
try {
|
||||
const webUrl = 'http://localhost:3737/HumanAgent';
|
||||
@@ -577,20 +596,8 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
overrideFileExists = fs.existsSync(overrideFilePath);
|
||||
}
|
||||
|
||||
const messagesHtml = this.messages.map(message => {
|
||||
const messageClass = message.sender === 'agent' ? 'ai-message' : 'human-message';
|
||||
const timestamp = message.timestamp.toLocaleTimeString();
|
||||
const senderLabel = message.sender === 'agent' ? 'AI' : 'Human';
|
||||
return `
|
||||
<div class="message ${messageClass}">
|
||||
<div class="message-header">
|
||||
<span class="sender">${senderLabel}</span>
|
||||
<span class="timestamp">${timestamp}</span>
|
||||
</div>
|
||||
<div class="message-content">${this._escapeHtml(String(message.content || ''))}</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
// Messages will be loaded dynamically from server via JavaScript
|
||||
const messagesHtml = '<div id="messages-loading">Loading conversation history...</div>';
|
||||
|
||||
const hasPendingResponse = this.currentRequestId ? 'waiting' : '';
|
||||
|
||||
@@ -897,28 +904,7 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
const message = input.value.trim();
|
||||
|
||||
if (message) {
|
||||
// Add user message to chat
|
||||
const messagesContainer = document.getElementById('messages');
|
||||
if (messagesContainer) {
|
||||
// Remove waiting indicator
|
||||
const waitingIndicator = messagesContainer.querySelector('.waiting-indicator');
|
||||
if (waitingIndicator) {
|
||||
waitingIndicator.remove();
|
||||
}
|
||||
|
||||
// Add user message
|
||||
const userMessageDiv = document.createElement('div');
|
||||
userMessageDiv.className = 'message user-message';
|
||||
userMessageDiv.innerHTML = \`
|
||||
<div class="message-header">
|
||||
<strong>You</strong>
|
||||
<span class="timestamp">\${new Date().toLocaleTimeString()}</span>
|
||||
</div>
|
||||
<div class="message-content">\${message.replace(/\\n/g, '<br>')}</div>
|
||||
\`;
|
||||
messagesContainer.appendChild(userMessageDiv);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
}
|
||||
// Don't add message to DOM - let SSE handle it to avoid duplicates
|
||||
|
||||
// Send message to extension
|
||||
vscode.postMessage({
|
||||
@@ -1063,6 +1049,8 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
handleRequestStateChange(data.data);
|
||||
} else if (data.type === 'chat_message') {
|
||||
handleIncomingChatMessage(data);
|
||||
} else if (data.type === 'session-name-changed') {
|
||||
handleSessionNameChanged(data.data);
|
||||
// Removed web_user_message auto-trigger - no longer needed
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1145,11 +1133,6 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
function handleIncomingChatMessage(data) {
|
||||
console.log('Handling incoming chat message:', data);
|
||||
|
||||
// Only process messages for the current session
|
||||
// For VS Code webview, we need to check if this is our session
|
||||
// This is a basic implementation - in a more complex setup,
|
||||
// we'd want proper session management
|
||||
|
||||
const message = data.message;
|
||||
const messagesContainer = document.getElementById('messages');
|
||||
if (messagesContainer) {
|
||||
@@ -1159,22 +1142,8 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
emptyState.remove();
|
||||
}
|
||||
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = \`message \${message.sender === 'user' ? 'user-message' : 'ai-message'}\`;
|
||||
|
||||
const displayName = message.sender === 'user' ? 'You' : 'Assistant';
|
||||
const timestamp = new Date(message.timestamp).toLocaleTimeString();
|
||||
|
||||
messageDiv.innerHTML = \`
|
||||
<div class="message-header">
|
||||
<strong>\${displayName}</strong>
|
||||
<span class="timestamp">\${timestamp}</span>
|
||||
</div>
|
||||
<div class="message-content">\${message.content.replace(/\\n/g, '<br>')}</div>
|
||||
\`;
|
||||
|
||||
messagesContainer.appendChild(messageDiv);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
// Use unified addMessageToUI function
|
||||
addMessageToUI(message);
|
||||
|
||||
// Play notification for assistant messages
|
||||
if (message.sender === 'agent') {
|
||||
@@ -1183,8 +1152,94 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSessionNameChanged(data) {
|
||||
console.log('Handling session name change:', data);
|
||||
|
||||
// Update the session name display in VS Code interface
|
||||
// The data contains: { sessionId, name }
|
||||
if (data.sessionId && data.name) {
|
||||
// Update window title or header display if needed
|
||||
// For VS Code webview, we can send a message to the extension
|
||||
vscode.postMessage({
|
||||
command: 'sessionNameUpdated',
|
||||
sessionId: data.sessionId,
|
||||
name: data.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// handleWebUserMessage removed - no longer needed for auto-forwarding
|
||||
|
||||
// Load conversation history from server
|
||||
async function loadConversationHistory() {
|
||||
try {
|
||||
const response = await fetch('http://127.0.0.1:3737/sessions/${this.workspaceSessionId}/messages');
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const messagesContainer = document.getElementById('messages');
|
||||
if (messagesContainer && data.messages) {
|
||||
// Clear loading indicator
|
||||
messagesContainer.innerHTML = '';
|
||||
|
||||
// Add each message using the same logic as web interface
|
||||
for (const msg of data.messages) {
|
||||
addMessageToUI(msg);
|
||||
}
|
||||
|
||||
console.log(\`Loaded \${data.messages.length} messages from server\`);
|
||||
} else {
|
||||
// Show empty state
|
||||
messagesContainer.innerHTML = '<div class="empty-state">No messages yet. Start a conversation!</div>';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load conversation history:', error);
|
||||
document.getElementById('messages').innerHTML = '<div class="error-state">Failed to load conversation history</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// Add message to UI (similar to web interface)
|
||||
function addMessageToUI(message) {
|
||||
const messagesContainer = document.getElementById('messages');
|
||||
if (!messagesContainer) return;
|
||||
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = \`message \${message.sender === 'user' ? 'human-message' : 'ai-message'}\`;
|
||||
|
||||
// Determine sender label based on sender and source
|
||||
let senderLabel = 'AI';
|
||||
if (message.sender === 'user') {
|
||||
if (message.source === 'vscode') {
|
||||
senderLabel = 'You (VS Code)';
|
||||
} else {
|
||||
senderLabel = 'You (Web)';
|
||||
}
|
||||
}
|
||||
|
||||
const timestamp = new Date(message.timestamp).toLocaleTimeString();
|
||||
|
||||
messageDiv.innerHTML = \`
|
||||
<div class="message-header">
|
||||
<span class="sender">\${senderLabel}</span>
|
||||
<span class="timestamp">\${timestamp}</span>
|
||||
</div>
|
||||
<div class="message-content">\${escapeHtml(message.content)}</div>
|
||||
\`;
|
||||
|
||||
messagesContainer.appendChild(messageDiv);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
}
|
||||
|
||||
// Escape HTML helper function
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Load conversation history on page load
|
||||
loadConversationHistory();
|
||||
|
||||
// Initialize SSE connection
|
||||
setupSSEConnection();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user