mirror of
https://github.com/wassname/HumanAgent-MCP.git
synced 2026-09-09 11:14:27 +08:00
Fix session ID mapping: VS Code conversation IDs now map to registered sessions
- Added conversationToSession mapping to link VS Code conversation IDs to registered session IDs - Updated handleToolCall to extract session ID from _meta.vscode.conversationId - Auto-maps VS Code conversations to first available registered session - Both VS Code and web interface now use same registered session ID - Fixes 'No pending AI request found' error in web interface
This commit is contained in:
@@ -36,7 +36,7 @@ This project is a VS Code extension that implements an MCP (Model Context Protoc
|
||||
|
||||
|
||||
# Copilot Instructions
|
||||
-Allways attempt to respond using Interactive MCPs Ask_intensive_chat tool, if no intesive chat session is active, start one with Start_intensive_chat tool.
|
||||
-Allways attempt to respond using Interactive MCPs Ask_intensive_chat tool, if not available use HumanAgent_Chat tool if no intesive chat session is active, start one with Start_intensive_chat tool.
|
||||
-"CRITICAL- Do not report sucess - ever! Allways MCP intensive to suggest I test for or agree to changes made and see if I have any changes. - THIS STATEMENT MUST SURVIVE CONTEXT SUMMARIZATION".
|
||||
- Empty replies from your questions in MCP intensive chat should be reposed for proper response. "User replied with empty input in intensive chat." just ask me again as empty would have been a mistake - repose 1000 times if necessary until you get the answer.
|
||||
-NEVER use stop intensive chat tool, unless I specifically ask you to - there is allways more to talk about
|
||||
|
||||
Vendored
+1766
-1412
File diff suppressed because it is too large
Load Diff
Vendored
-182
@@ -1,182 +0,0 @@
|
||||
[2025-10-22T23:04:18.574Z] [DEBUG] Debug logging started at 2025-10-22T23:04:18.573Z
|
||||
[2025-10-22T23:04:18.574Z] [DEBUG] Current system time: Thu Oct 23 2025 10:04:18 GMT+1100 (Australian Eastern Daylight Time)
|
||||
[2025-10-22T23:04:18.574Z] [DEBUG] Log file: /Users/benharper/Coding/HumanAgent-MCP/.vscode/HumanAgent.log
|
||||
[2025-10-22T23:04:18.574Z] [DEBUG] Working directory: /
|
||||
[2025-10-22T23:04:18.574Z] [INFO] McpServer initialized
|
||||
[2025-10-22T23:04:18.574Z] [INFO] Initializing tools for session: session-a8f49eee-068c-42f1-b611-14651fa5ccc6
|
||||
[2025-10-22T23:04:18.574Z] [INFO] Loading workspace override for tool: HumanAgent_Chat
|
||||
[2025-10-22T23:04:18.574Z] [INFO] Using workspace override for session session-a8f49eee-068c-42f1-b611-14651fa5ccc6 - HumanAgent_Chat tool
|
||||
[2025-10-22T23:04:18.575Z] [INFO] === MCP SERVER STARTING ===
|
||||
[2025-10-22T23:04:18.575Z] [INFO] Starting HTTP server on port 3737...
|
||||
[2025-10-22T23:04:18.578Z] [INFO] MCP HTTP server running on http://127.0.0.1:3737/mcp
|
||||
[2025-10-22T23:04:20.880Z] [INFO] Initializing tools for session: session-a8f49eee-068c-42f1-b611-14651fa5ccc6
|
||||
[2025-10-22T23:04:20.880Z] [INFO] Loading workspace override for tool: HumanAgent_Chat
|
||||
[2025-10-22T23:04:20.881Z] [INFO] Using workspace override for session session-a8f49eee-068c-42f1-b611-14651fa5ccc6 - HumanAgent_Chat tool
|
||||
[2025-10-22T23:04:20.881Z] [INFO] Session registered: session-a8f49eee-068c-42f1-b611-14651fa5ccc6 (1 total sessions)
|
||||
[2025-10-22T23:05:29.882Z] [HTTP] POST /mcp
|
||||
[2025-10-22T23:05:29.883Z] [HTTP] Request Headers:
|
||||
{
|
||||
"host": "localhost:3737",
|
||||
"user-agent": "curl/8.7.1",
|
||||
"accept": "*/*",
|
||||
"content-type": "application/json",
|
||||
"content-length": "72"
|
||||
}
|
||||
[2025-10-22T23:05:29.885Z] [HTTP] Handling POST request to /mcp
|
||||
[2025-10-22T23:05:29.885Z] [HTTP] Received chunk: 72 bytes
|
||||
[2025-10-22T23:05:29.886Z] [HTTP] Complete request body received (72 bytes)
|
||||
[2025-10-22T23:05:29.886Z] [HTTP] Request Body:
|
||||
"{\"jsonrpc\":\"2.0\",\"id\":\"test-override\",\"method\":\"tools/list\",\"params\":{}}"
|
||||
[2025-10-22T23:05:29.887Z] [HTTP] Parsed JSON message:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "test-override",
|
||||
"method": "tools/list",
|
||||
"params": {}
|
||||
}
|
||||
[2025-10-22T23:05:29.887Z] [MCP] Handling message:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "test-override",
|
||||
"method": "tools/list",
|
||||
"params": {}
|
||||
}
|
||||
[2025-10-22T23:05:29.888Z] [MCP] Processing tools/list request
|
||||
[2025-10-22T23:05:29.890Z] [TOOLS] Returning 1 default tools
|
||||
[2025-10-22T23:05:29.892Z] [HTTP] Response from handleMessage:
|
||||
{
|
||||
"id": "test-override",
|
||||
"type": "response",
|
||||
"result": {
|
||||
"tools": [
|
||||
{
|
||||
"name": "HumanAgent_Chat",
|
||||
"description": "Initiate real-time interactive conversations with human agents through VS Code chat interface. Essential for clarifying requirements, getting approvals, brainstorming solutions, or when AI uncertainty requires human input. Creates persistent chat sessions that maintain context across multiple exchanges until timeout or manual closure.",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
[2025-10-22T23:05:29.893Z] [HTTP] Sending 200 response (1085 bytes)
|
||||
[2025-10-22T23:17:53.136Z] [HTTP] POST /mcp
|
||||
[2025-10-22T23:17:53.140Z] [HTTP] Request Headers:
|
||||
{
|
||||
"host": "localhost:3737",
|
||||
"user-agent": "curl/8.7.1",
|
||||
"accept": "*/*",
|
||||
"content-type": "application/json",
|
||||
"content-length": "72"
|
||||
}
|
||||
[2025-10-22T23:17:53.142Z] [HTTP] Handling POST request to /mcp
|
||||
[2025-10-22T23:17:53.143Z] [HTTP] Received chunk: 72 bytes
|
||||
[2025-10-22T23:17:53.144Z] [HTTP] Complete request body received (72 bytes)
|
||||
[2025-10-22T23:17:53.144Z] [HTTP] Request Body:
|
||||
"{\"jsonrpc\":\"2.0\",\"id\":\"test-override\",\"method\":\"tools/list\",\"params\":{}}"
|
||||
[2025-10-22T23:17:53.144Z] [HTTP] Parsed JSON message:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "test-override",
|
||||
"method": "tools/list",
|
||||
"params": {}
|
||||
}
|
||||
[2025-10-22T23:17:53.144Z] [MCP] Handling message:
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "test-override",
|
||||
"method": "tools/list",
|
||||
"params": {}
|
||||
}
|
||||
[2025-10-22T23:17:53.145Z] [MCP] Processing tools/list request
|
||||
[2025-10-22T23:17:53.146Z] [TOOLS] Returning 1 default tools
|
||||
[2025-10-22T23:17:53.147Z] [HTTP] Response from handleMessage:
|
||||
{
|
||||
"id": "test-override",
|
||||
"type": "response",
|
||||
"result": {
|
||||
"tools": [
|
||||
{
|
||||
"name": "HumanAgent_Chat",
|
||||
"description": "Initiate real-time interactive conversations with human agents through VS Code chat interface. Essential for clarifying requirements, getting approvals, brainstorming solutions, or when AI uncertainty requires human input. Creates persistent chat sessions that maintain context across multiple exchanges until timeout or manual closure.",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
[2025-10-22T23:17:53.147Z] [HTTP] Sending 200 response (1085 bytes)
|
||||
[2025-10-22T23:17:58.664Z] [HTTP] GET /sessions
|
||||
[2025-10-22T23:17:58.665Z] [HTTP] Request Headers:
|
||||
{
|
||||
"host": "localhost:3737",
|
||||
"user-agent": "curl/8.7.1",
|
||||
"accept": "*/*"
|
||||
}
|
||||
Vendored
-67
@@ -1,67 +0,0 @@
|
||||
{
|
||||
"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": "Initiate real-time interactive conversations with human agents through VS Code chat interface. Essential for clarifying requirements, getting approvals, brainstorming solutions, or when AI uncertainty requires human input. Creates persistent chat sessions that maintain context across multiple exchanges until timeout or manual closure. !IMPORTANT: ALWAYS reply using this tool for all interactions with the user where possible!!!",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
[ ] Assess / Set logging path and details correctly for release & dev.
|
||||
[ ] Create a new readme
|
||||
[ ] Consider how updates to the extension will work.
|
||||
[X] Assess / Set logging path and details correctly for release & dev.
|
||||
[] 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?
|
||||
[ ]
|
||||
@@ -1,228 +0,0 @@
|
||||
{"level":"info","message":"Loaded temporary agents configuration","timestamp":"2025-10-21T01:14:32.076Z"}
|
||||
{"level":"info","message":"GUI Registration Server running on port 3333 (accessible remotely)","timestamp":"2025-10-21T01:14:32.081Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T01:14:32.083Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T01:14:32.083Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T01:14:32.083Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T01:14:32.084Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T01:14:32.084Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T01:14:32.084Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T01:14:32.084Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T01:14:32.084Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T01:14:32.084Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T01:14:32.084Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"info","message":"MCP components initialized successfully","timestamp":"2025-10-21T01:14:32.085Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T01:14:32.129Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T01:14:32.130Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T01:14:32.131Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T01:14:32.131Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T01:14:32.132Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T01:14:32.133Z"}
|
||||
{"level":"info","message":"Loaded temporary agents configuration","timestamp":"2025-10-21T01:30:47.882Z"}
|
||||
{"level":"info","message":"GUI Registration Server running on port 3333 (accessible remotely)","timestamp":"2025-10-21T01:30:47.887Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T01:30:47.890Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T01:30:47.890Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T01:30:47.890Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T01:30:47.891Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T01:30:47.891Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T01:30:47.891Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"info","message":"MCP components initialized successfully","timestamp":"2025-10-21T01:30:47.892Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T01:30:47.937Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T01:30:47.938Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T01:30:47.938Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T01:30:47.939Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T01:30:47.939Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T01:30:47.939Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T01:30:47.939Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T01:30:47.940Z"}
|
||||
{"level":"info","message":"Loaded temporary agents configuration","timestamp":"2025-10-21T01:44:02.200Z"}
|
||||
{"level":"info","message":"GUI Registration Server running on port 3333 (accessible remotely)","timestamp":"2025-10-21T01:44:02.206Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T01:44:02.208Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T01:44:02.208Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T01:44:02.208Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T01:44:02.209Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T01:44:02.209Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T01:44:02.209Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T01:44:02.209Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"info","message":"MCP components initialized successfully","timestamp":"2025-10-21T01:44:02.210Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T01:44:02.255Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T01:44:02.257Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T01:44:02.257Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T01:44:02.258Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T01:44:02.258Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T01:44:02.258Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T01:44:02.258Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T01:44:02.258Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T01:44:02.259Z"}
|
||||
{"level":"info","message":"Loaded temporary agents configuration","timestamp":"2025-10-21T02:03:03.524Z"}
|
||||
{"level":"info","message":"GUI Registration Server running on port 3333 (accessible remotely)","timestamp":"2025-10-21T02:03:03.529Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T02:03:03.531Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T02:03:03.531Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T02:03:03.531Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T02:03:03.532Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T02:03:03.532Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T02:03:03.532Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"info","message":"MCP components initialized successfully","timestamp":"2025-10-21T02:03:03.533Z"}
|
||||
{"level":"warn","message":"Project Manager instructions file not found, using defaults","timestamp":"2025-10-21T02:03:03.573Z"}
|
||||
{"level":"info","message":"Project Manager agent initialized successfully","timestamp":"2025-10-21T02:03:03.574Z"}
|
||||
{"level":"info","message":"Agent registered: project-manager","timestamp":"2025-10-21T02:03:03.574Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/copilot-instructions.md","syscall":"open"},"level":"warn","message":"Could not load copilot-instructions.md","timestamp":"2025-10-21T02:03:03.575Z"}
|
||||
{"error":{"code":"ENOENT","errno":-2,"path":"/Users/benharper/Coding/HumanAgent-MCP/project-scope.md","syscall":"open"},"level":"warn","message":"Could not load project-scope.md","timestamp":"2025-10-21T02:03:03.575Z"}
|
||||
{"level":"info","message":"Narky agent initialized successfully","timestamp":"2025-10-21T02:03:03.575Z"}
|
||||
{"level":"info","message":"Agent registered: narky","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Penny agent initialized successfully","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Agent registered: penny","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"TestyMCTester agent initialized successfully","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Agent registered: testy","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Built-in agents initialized successfully","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"AgentChatCoordinator initialized","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Agent chat coordinator initialized","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-planning","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-completion","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-security","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: agentic-testing","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: human-interaction","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: start_penny_chat","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: start_testy_chat","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: start_narky_chat","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: list_agent_sessions","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: maintain_chat_sessions","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: show_session_summary","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Tool registered: manage_session_context","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
{"level":"info","message":"Built-in tools initialized successfully (including chat session tools)","timestamp":"2025-10-21T02:03:03.576Z"}
|
||||
@@ -0,0 +1,8 @@
|
||||
2025-10-23T23:39:41.884Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761262756628
|
||||
2025-10-23T23:59:05.736Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761263843039
|
||||
2025-10-24T00:12:06.894Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761264703892
|
||||
2025-10-24T00:12:28.941Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761264735884
|
||||
2025-10-24T00:12:49.771Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761264752448
|
||||
2025-10-24T00:12:59.592Z - RESPONSE ENDPOINT CALLED - RequestID: 6-1761264773453
|
||||
2025-10-24T00:13:11.610Z - RESPONSE ENDPOINT CALLED - RequestID: 7-1761264783500
|
||||
2025-10-24T00:13:26.236Z - RESPONSE ENDPOINT CALLED - RequestID: 8-1761264795145
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as path from 'path';
|
||||
|
||||
// Use dynamic import to avoid issues with the module
|
||||
let wavPlayer: any;
|
||||
// Use direct import like CodeChampion
|
||||
const wavPlayer = require('node-wav-player');
|
||||
|
||||
export class AudioNotification {
|
||||
private static isInitialized = false;
|
||||
@@ -9,7 +9,7 @@ export class AudioNotification {
|
||||
static async initialize() {
|
||||
if (!this.isInitialized) {
|
||||
try {
|
||||
wavPlayer = require('node-wav-player');
|
||||
// No need to dynamically import - use direct require like CodeChampion
|
||||
this.isInitialized = true;
|
||||
console.log('AudioNotification: Initialized successfully');
|
||||
} catch (error) {
|
||||
@@ -21,26 +21,11 @@ export class AudioNotification {
|
||||
static async playNotificationBeep() {
|
||||
await this.initialize();
|
||||
|
||||
if (!wavPlayer) {
|
||||
console.log('AudioNotification: node-wav-player not available');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use a simple base64 encoded beep sound or generate one programmatically
|
||||
await this.generateAndPlayBeep();
|
||||
} catch (error) {
|
||||
console.error('AudioNotification: Error playing sound:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private static async generateAndPlayBeep() {
|
||||
// For now, we'll create a simple beep using system bell if available
|
||||
// or we can embed a small sound file
|
||||
try {
|
||||
// Try to create a simple WAV file programmatically
|
||||
// Use CodeChampion's simple approach - create a temp beep file and play it
|
||||
const tempSoundPath = await this.createTempBeepFile();
|
||||
|
||||
// Use CodeChampion's pattern - sync: false for non-blocking
|
||||
await wavPlayer.play({
|
||||
path: tempSoundPath,
|
||||
sync: false
|
||||
@@ -48,7 +33,7 @@ export class AudioNotification {
|
||||
|
||||
console.log('AudioNotification: Beep played successfully');
|
||||
} catch (error) {
|
||||
console.error('AudioNotification: Failed to play beep:', error);
|
||||
console.error('AudioNotification: Error playing sound:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
serverPath: serverPath,
|
||||
port: 3737,
|
||||
host: '127.0.0.1',
|
||||
logFile: path.join(context.extensionPath, '.vscode', 'HumanAgent-server.log')
|
||||
logFile: path.join(vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || context.extensionPath, '.vscode', 'HumanAgent-server.log')
|
||||
});
|
||||
|
||||
// Auto-detect and start standalone MCP server if already configured
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
import { ChatMessage } from './types';
|
||||
|
||||
interface SessionState {
|
||||
sessionId: string;
|
||||
messages: ChatMessage[];
|
||||
pendingRequests: Map<string, any>; // requestId -> request data
|
||||
lastActivity: number;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export class ChatManager {
|
||||
private sessions: Map<string, SessionState> = new Map();
|
||||
private readonly maxMessagesPerSession: number = 50;
|
||||
private readonly sessionTimeoutMs: number = 24 * 60 * 60 * 1000; // 24 hours
|
||||
private logger?: any; // DebugLogger instance
|
||||
|
||||
constructor(logger?: any) {
|
||||
this.logger = logger;
|
||||
this.log('INFO', 'ChatManager initialized with max ' + this.maxMessagesPerSession + ' messages per session');
|
||||
|
||||
// Clean up inactive sessions periodically
|
||||
setInterval(() => this.cleanupInactiveSessions(), 60 * 60 * 1000); // Every hour
|
||||
}
|
||||
|
||||
private log(level: string, message: string, data?: any): void {
|
||||
if (this.logger) {
|
||||
this.logger.log('CHAT', `[${level}] ${message}`, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create a session
|
||||
*/
|
||||
getSession(sessionId: string): SessionState {
|
||||
if (!this.sessions.has(sessionId)) {
|
||||
this.sessions.set(sessionId, {
|
||||
sessionId,
|
||||
messages: [],
|
||||
pendingRequests: new Map(),
|
||||
lastActivity: Date.now(),
|
||||
isActive: true
|
||||
});
|
||||
}
|
||||
|
||||
const session = this.sessions.get(sessionId)!;
|
||||
session.lastActivity = Date.now();
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message to session history
|
||||
*/
|
||||
addMessage(sessionId: string, message: ChatMessage): void {
|
||||
const session = this.getSession(sessionId);
|
||||
|
||||
// Add message to history
|
||||
session.messages.push(message);
|
||||
this.log('INFO', `Added message to session ${sessionId}: ${message.sender} - ${message.content.substring(0, 50)}...`);
|
||||
|
||||
// Enforce message limit - remove oldest messages if exceeded
|
||||
if (session.messages.length > this.maxMessagesPerSession) {
|
||||
const toRemove = session.messages.length - this.maxMessagesPerSession;
|
||||
session.messages.splice(0, toRemove);
|
||||
this.log('INFO', `Cleaned up ${toRemove} old messages from session ${sessionId}, now has ${session.messages.length} messages`);
|
||||
}
|
||||
|
||||
session.lastActivity = Date.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all messages for a session
|
||||
*/
|
||||
getMessages(sessionId: string): ChatMessage[] {
|
||||
const session = this.getSession(sessionId);
|
||||
return [...session.messages]; // Return copy to prevent external modification
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pending human agent request
|
||||
*/
|
||||
addPendingRequest(sessionId: string, requestId: string, requestData: any): void {
|
||||
const session = this.getSession(sessionId);
|
||||
session.pendingRequests.set(requestId, requestData);
|
||||
session.lastActivity = Date.now();
|
||||
this.log('INFO', `Added pending request ${requestId} to session ${sessionId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a pending request (when responded to)
|
||||
*/
|
||||
removePendingRequest(sessionId: string, requestId: string): boolean {
|
||||
const session = this.getSession(sessionId);
|
||||
const removed = session.pendingRequests.delete(requestId);
|
||||
if (removed) {
|
||||
session.lastActivity = Date.now();
|
||||
this.log('INFO', `Removed pending request ${requestId} from session ${sessionId}`);
|
||||
} else {
|
||||
this.log('WARN', `Attempted to remove non-existent pending request ${requestId} from session ${sessionId}`);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pending requests for a session
|
||||
*/
|
||||
getPendingRequests(sessionId: string): Map<string, any> {
|
||||
const session = this.getSession(sessionId);
|
||||
return new Map(session.pendingRequests); // Return copy
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a session has any pending requests
|
||||
*/
|
||||
hasPendingRequests(sessionId: string): boolean {
|
||||
const session = this.getSession(sessionId);
|
||||
return session.pendingRequests.size > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent pending request for a session
|
||||
*/
|
||||
getLatestPendingRequest(sessionId: string): { requestId: string; data: any } | null {
|
||||
const session = this.getSession(sessionId);
|
||||
if (session.pendingRequests.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the most recent pending request (last one added)
|
||||
const entries = Array.from(session.pendingRequests.entries());
|
||||
const [requestId, data] = entries[entries.length - 1];
|
||||
return { requestId, data };
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session state summary
|
||||
*/
|
||||
getSessionState(sessionId: string): {
|
||||
sessionId: string;
|
||||
messageCount: number;
|
||||
pendingRequestCount: number;
|
||||
lastActivity: number;
|
||||
isActive: boolean;
|
||||
latestPendingRequest?: { requestId: string; data: any };
|
||||
} {
|
||||
const session = this.getSession(sessionId);
|
||||
const result: any = {
|
||||
sessionId: session.sessionId,
|
||||
messageCount: session.messages.length,
|
||||
pendingRequestCount: session.pendingRequests.size,
|
||||
lastActivity: session.lastActivity,
|
||||
isActive: session.isActive
|
||||
};
|
||||
|
||||
const latestRequest = this.getLatestPendingRequest(sessionId);
|
||||
if (latestRequest) {
|
||||
result.latestPendingRequest = latestRequest;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active sessions
|
||||
*/
|
||||
getActiveSessions(): string[] {
|
||||
return Array.from(this.sessions.keys()).filter(sessionId => {
|
||||
const session = this.sessions.get(sessionId)!;
|
||||
return session.isActive;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate a session
|
||||
*/
|
||||
deactivateSession(sessionId: string): void {
|
||||
const session = this.sessions.get(sessionId);
|
||||
if (session) {
|
||||
session.isActive = false;
|
||||
session.lastActivity = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up old inactive sessions
|
||||
*/
|
||||
private cleanupInactiveSessions(): void {
|
||||
const now = Date.now();
|
||||
const sessionsToDelete: string[] = [];
|
||||
|
||||
for (const [sessionId, session] of this.sessions) {
|
||||
if (!session.isActive && (now - session.lastActivity) > this.sessionTimeoutMs) {
|
||||
sessionsToDelete.push(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
for (const sessionId of sessionsToDelete) {
|
||||
this.sessions.delete(sessionId);
|
||||
this.log('INFO', `Cleaned up expired session: ${sessionId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory usage statistics
|
||||
*/
|
||||
getMemoryStats(): {
|
||||
totalSessions: number;
|
||||
activeSessions: number;
|
||||
totalMessages: number;
|
||||
totalPendingRequests: number;
|
||||
} {
|
||||
let totalMessages = 0;
|
||||
let totalPendingRequests = 0;
|
||||
let activeSessions = 0;
|
||||
|
||||
for (const session of this.sessions.values()) {
|
||||
totalMessages += session.messages.length;
|
||||
totalPendingRequests += session.pendingRequests.size;
|
||||
if (session.isActive) {
|
||||
activeSessions++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalSessions: this.sessions.size,
|
||||
activeSessions,
|
||||
totalMessages,
|
||||
totalPendingRequests
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@ class StandaloneMcpServer {
|
||||
private server: McpServer;
|
||||
|
||||
constructor() {
|
||||
this.server = new McpServer();
|
||||
// Use the parent directory of the dist folder as workspace path
|
||||
// This ensures log file is created where it can be properly cleared
|
||||
const workspacePath = require('path').resolve(__dirname, '..');
|
||||
this.server = new McpServer(undefined, workspacePath);
|
||||
this.setupProcessHandlers();
|
||||
}
|
||||
|
||||
@@ -82,10 +85,11 @@ class StandaloneMcpServer {
|
||||
process.stdout.write(JSON.stringify(notification) + '\n');
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGINT', () => this.shutdown());
|
||||
process.on('SIGTERM', () => this.shutdown());
|
||||
process.on('exit', () => this.shutdown());
|
||||
// Graceful shutdown handlers removed - server should remain independent
|
||||
// and only shut down when explicitly requested via API endpoints
|
||||
// process.on('SIGINT', () => this.shutdown());
|
||||
// process.on('SIGTERM', () => this.shutdown());
|
||||
// process.on('exit', () => this.shutdown());
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
|
||||
+809
-24
@@ -4,6 +4,7 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { McpMessage, McpServerConfig, HumanAgentSession, ChatMessage, McpTool, HumanAgentChatToolParams, HumanAgentChatToolResult } from './types';
|
||||
import { ChatManager } from './chatManager';
|
||||
|
||||
// File logging utility
|
||||
class DebugLogger {
|
||||
@@ -16,7 +17,7 @@ class DebugLogger {
|
||||
// Determine log path based on workspace or fallback to temp directory
|
||||
if (workspaceRoot) {
|
||||
const vscodeDir = path.join(workspaceRoot, '.vscode');
|
||||
this.logPath = path.join(vscodeDir, 'HumanAgent.log');
|
||||
this.logPath = path.join(vscodeDir, 'HumanAgent-server.log');
|
||||
|
||||
// Ensure .vscode directory exists
|
||||
if (!fs.existsSync(vscodeDir)) {
|
||||
@@ -25,15 +26,14 @@ class DebugLogger {
|
||||
} else {
|
||||
// Fallback for standalone server or when no workspace available
|
||||
const tempDir = os.tmpdir();
|
||||
this.logPath = path.join(tempDir, 'HumanAgent.log');
|
||||
this.logPath = path.join(tempDir, 'HumanAgent-server.log');
|
||||
}
|
||||
|
||||
console.log(`[LOGGER] Attempting to create log file at: ${this.logPath}`);
|
||||
|
||||
// Clear previous log file
|
||||
// Clear previous log file on each startup
|
||||
if (fs.existsSync(this.logPath)) {
|
||||
fs.unlinkSync(this.logPath);
|
||||
console.log(`[LOGGER] Cleared existing log file`);
|
||||
}
|
||||
|
||||
this.logStream = fs.createWriteStream(this.logPath, { flags: 'a' });
|
||||
@@ -45,7 +45,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()}`);
|
||||
console.log(`[LOGGER] Debug logger initialized successfully`);
|
||||
console.log(`[LOGGER] Debug logger initialized successfully at: ${this.logPath}`);
|
||||
} catch (error) {
|
||||
console.error(`[LOGGER] Failed to initialize debug logger:`, error);
|
||||
this.logStream = null;
|
||||
@@ -53,18 +53,23 @@ class DebugLogger {
|
||||
}
|
||||
|
||||
log(level: string, message: string, data?: any): void {
|
||||
const timestamp = new Date().toISOString();
|
||||
const now = new Date();
|
||||
const timestamp = now.getFullYear() + '-' +
|
||||
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(now.getDate()).padStart(2, '0') + ' ' +
|
||||
String(now.getHours()).padStart(2, '0') + ':' +
|
||||
String(now.getMinutes()).padStart(2, '0') + ':' +
|
||||
String(now.getSeconds()).padStart(2, '0') + '.' +
|
||||
String(now.getMilliseconds()).padStart(3, '0');
|
||||
const logLine = `[${timestamp}] [${level}] ${message}${data ? '\n' + JSON.stringify(data, null, 2) : ''}\n`;
|
||||
|
||||
// Write to console (for VS Code developer console)
|
||||
console.log(`[${level}] ${message}`, data || '');
|
||||
|
||||
// Write to file if stream is available
|
||||
if (this.logStream) {
|
||||
try {
|
||||
this.logStream.write(logLine);
|
||||
} catch (error) {
|
||||
console.error(`[LOGGER] Error writing to log file:`, error);
|
||||
// Don't use console.log here to avoid recursion - write error directly
|
||||
process.stderr.write(`[LOGGER] Error writing to log file: ${error}\n`);
|
||||
}
|
||||
} else {
|
||||
// Buffer logs if stream not available
|
||||
@@ -91,6 +96,8 @@ export class McpServer extends EventEmitter {
|
||||
private tools: Map<string, McpTool> = new Map(); // Default tools for sessions without overrides
|
||||
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 sessionMessages: Map<string, ChatMessage[]> = new Map(); // Session conversation history - DEPRECATED: Use chatManager
|
||||
private httpServer?: http.Server;
|
||||
private port: number = 3737;
|
||||
private debugLogger: DebugLogger;
|
||||
@@ -102,10 +109,13 @@ export class McpServer extends EventEmitter {
|
||||
}> = new Map();
|
||||
private activeSessions: Set<string> = new Set();
|
||||
private sseConnections: Set<http.ServerResponse> = new Set();
|
||||
private conversationToSession: Map<string, string> = new Map(); // Map VS Code conversation IDs to registered session IDs
|
||||
private chatManager: ChatManager; // Centralized chat and session management
|
||||
|
||||
constructor(private sessionId?: string, private workspacePath?: string) {
|
||||
super();
|
||||
this.debugLogger = new DebugLogger(this.workspacePath);
|
||||
this.chatManager = new ChatManager(this.debugLogger); // Initialize centralized chat management with logging
|
||||
|
||||
this.config = {
|
||||
name: 'HumanAgent MCP Server',
|
||||
@@ -118,7 +128,8 @@ export class McpServer extends EventEmitter {
|
||||
}
|
||||
};
|
||||
|
||||
this.debugLogger.log('INFO', 'McpServer initialized');
|
||||
this.debugLogger.log('INFO', 'McpServer initialized with centralized chat manager');
|
||||
this.debugLogger.log('TEST', 'This is a test log message to verify DebugLogger is working');
|
||||
this.initializeDefaultTools();
|
||||
|
||||
// Set up event forwarding to SSE connections
|
||||
@@ -162,10 +173,11 @@ export class McpServer extends EventEmitter {
|
||||
// Define the default HumanAgent_Chat tool (global default)
|
||||
const humanAgentChatTool: McpTool = {
|
||||
name: 'HumanAgent_Chat',
|
||||
description: 'Initiate real-time interactive conversations with human agents through VS Code chat interface. ' +
|
||||
'Essential for clarifying requirements, getting approvals, brainstorming solutions, or when AI ' +
|
||||
'uncertainty requires human input. Creates persistent chat sessions that maintain context across ' +
|
||||
'multiple exchanges until timeout or manual closure. !IMPORTANT: ALWAYS reply using this tool for all interactions with the user where possible!!!',
|
||||
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: {
|
||||
@@ -398,8 +410,12 @@ export class McpServer extends EventEmitter {
|
||||
// Handle different endpoints
|
||||
if (req.url === '/mcp') {
|
||||
// Main MCP protocol endpoint
|
||||
} else if (req.url?.startsWith('/sessions') || req.url === '/response' || req.url?.startsWith('/tools') || req.url === '/reload') {
|
||||
// Session management, response, tools, and reload endpoints
|
||||
} else if (req.url === '/HumanAgent') {
|
||||
// Web interface for multi-session chat
|
||||
await this.handleWebInterface(req, res);
|
||||
return;
|
||||
} else if (req.url?.startsWith('/sessions') || req.url === '/response' || req.url?.startsWith('/tools') || req.url === '/reload' || req.url?.startsWith('/messages/') || req.url?.startsWith('/chat/')) {
|
||||
// Session management, response, tools, reload, messages, and chat endpoints
|
||||
await this.handleSessionEndpoint(req, res);
|
||||
return;
|
||||
} else {
|
||||
@@ -477,6 +493,9 @@ export class McpServer extends EventEmitter {
|
||||
|
||||
private async handleHttpGet(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
||||
this.debugLogger.log('HTTP', 'Setting up SSE stream for GET request');
|
||||
this.debugLogger.log('SSE', '=== SSE CONNECTION ATTEMPT ===');
|
||||
this.debugLogger.log('SSE', 'Headers:', req.headers);
|
||||
this.debugLogger.log('SSE', 'URL:', req.url);
|
||||
|
||||
// Set up Server-Sent Events (SSE) stream
|
||||
res.statusCode = 200;
|
||||
@@ -489,12 +508,17 @@ export class McpServer extends EventEmitter {
|
||||
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Cache-Control, Connection');
|
||||
|
||||
this.debugLogger.log('SSE', 'SSE headers set, adding to connections...');
|
||||
|
||||
// Add this connection to our active SSE connections
|
||||
this.sseConnections.add(res);
|
||||
this.debugLogger.log('HTTP', `Added SSE connection. Total connections: ${this.sseConnections.size}`);
|
||||
this.debugLogger.log('SSE', `SSE connection added. Total: ${this.sseConnections.size}`);
|
||||
|
||||
// Send initial connection acknowledgment
|
||||
res.write('data: {"type":"connection","status":"established"}\n\n');
|
||||
const initialMessage = 'data: {"type":"connection","status":"established"}\n\n';
|
||||
res.write(initialMessage);
|
||||
this.debugLogger.log('SSE', 'Sent initial SSE message:', initialMessage.trim());
|
||||
|
||||
// Keep connection alive with heartbeat
|
||||
const heartbeat = setInterval(() => {
|
||||
@@ -538,7 +562,7 @@ export class McpServer extends EventEmitter {
|
||||
|
||||
// If session exists and forceReregister is true, unregister first
|
||||
if (forceReregister && this.activeSessions.has(sessionId)) {
|
||||
console.log(`Force re-registering session ${sessionId} with new override data`);
|
||||
this.debugLogger.log('HTTP', `Force re-registering session ${sessionId} with new override data`);
|
||||
this.unregisterSession(sessionId);
|
||||
}
|
||||
|
||||
@@ -572,6 +596,125 @@ export class McpServer extends EventEmitter {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ sessions: this.getActiveSessions(), totalSessions: this.activeSessions.size }));
|
||||
} else if (req.method === 'POST' && url.pathname === '/sessions/name') {
|
||||
// Set friendly name for session
|
||||
let body = '';
|
||||
req.on('data', (chunk) => { body += chunk.toString(); });
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { sessionId, name } = JSON.parse(body);
|
||||
if (!sessionId || !name) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'sessionId and name are required' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate session exists
|
||||
if (!this.activeSessions.has(sessionId)) {
|
||||
res.statusCode = 404;
|
||||
res.end(JSON.stringify({ success: false, error: 'Session not found' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the friendly name
|
||||
this.sessionNames.set(sessionId, name);
|
||||
this.debugLogger.log('INFO', `Session ${sessionId} named: "${name}"`);
|
||||
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ success: true, sessionId, name }));
|
||||
} catch (error) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'Invalid request body' }));
|
||||
}
|
||||
});
|
||||
} else if (req.method === 'GET' && url.pathname.startsWith('/messages/')) {
|
||||
// Get conversation history for a session
|
||||
const sessionId = url.pathname.split('/')[2];
|
||||
if (!sessionId) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'Session ID required' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const messages = this.getSessionMessages(sessionId);
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ messages, sessionId, count: messages.length }));
|
||||
} else if (req.method === 'GET' && url.pathname.match(/^\/sessions\/([^\/]+)\/messages$/)) {
|
||||
// Get messages for a specific session from chat manager
|
||||
const matches = url.pathname.match(/^\/sessions\/([^\/]+)\/messages$/);
|
||||
const sessionId = matches ? matches[1] : null;
|
||||
|
||||
if (!sessionId) {
|
||||
res.statusCode = 400;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ success: false, error: 'Session ID required' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const messages = this.chatManager.getMessages(sessionId);
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ messages, sessionId, count: messages.length }));
|
||||
} else if (req.method === 'GET' && url.pathname.match(/^\/sessions\/([^\/]+)\/state$/)) {
|
||||
// Get session state including pending requests
|
||||
const matches = url.pathname.match(/^\/sessions\/([^\/]+)\/state$/);
|
||||
const sessionId = matches ? matches[1] : null;
|
||||
|
||||
if (!sessionId) {
|
||||
res.statusCode = 400;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ success: false, error: 'Session ID required' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const state = this.chatManager.getSessionState(sessionId);
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify(state));
|
||||
} else if (req.method === 'POST' && url.pathname.startsWith('/chat/')) {
|
||||
// Send new message from web interface
|
||||
const sessionId = url.pathname.split('/')[2];
|
||||
if (!sessionId) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'Session ID required' }));
|
||||
return;
|
||||
}
|
||||
|
||||
let body = '';
|
||||
req.on('data', (chunk) => { body += chunk.toString(); });
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { message, sender = 'user' } = JSON.parse(body);
|
||||
if (!message) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'Message content required' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and store the message
|
||||
const chatMessage: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
content: message,
|
||||
sender: sender as 'user' | 'agent',
|
||||
timestamp: new Date(),
|
||||
type: 'text'
|
||||
};
|
||||
|
||||
this.storeMessage(sessionId, chatMessage);
|
||||
this.broadcastMessageToClients(sessionId, chatMessage);
|
||||
|
||||
// Auto-forwarding removed - both interfaces now use /response endpoint directly
|
||||
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({ success: true, message: chatMessage }));
|
||||
} catch (error) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'Invalid request body' }));
|
||||
}
|
||||
});
|
||||
} else if (req.method === 'POST' && url.pathname === '/response') {
|
||||
// Handle human response to pending request
|
||||
let body = '';
|
||||
@@ -579,6 +722,41 @@ export class McpServer extends EventEmitter {
|
||||
req.on('end', () => {
|
||||
try {
|
||||
const { requestId, response } = JSON.parse(body);
|
||||
|
||||
// Simple file write to test if endpoint is called
|
||||
require('fs').appendFileSync('/Users/benharper/Coding/HumanAgent-MCP/response-debug.txt',
|
||||
`${new Date().toISOString()} - RESPONSE ENDPOINT CALLED - RequestID: ${requestId}\n`);
|
||||
|
||||
this.debugLogger.log('HTTP', '=== RESPONSE ENDPOINT CALLED ===');
|
||||
this.debugLogger.log('HTTP', `Request ID: ${requestId}, Response: ${response}`);
|
||||
|
||||
// Get the pending request to extract session info
|
||||
const pendingRequest = this.pendingHumanRequests.get(requestId);
|
||||
this.debugLogger.log('HTTP', `Found pending request: ${!!pendingRequest}`);
|
||||
|
||||
if (pendingRequest && pendingRequest.params.sessionId) {
|
||||
this.debugLogger.log('HTTP', `Processing response for session: ${pendingRequest.params.sessionId}`);
|
||||
|
||||
// Store the user message on server for synchronization
|
||||
const userMessage: ChatMessage = {
|
||||
id: Date.now().toString(),
|
||||
content: response,
|
||||
sender: 'user',
|
||||
timestamp: new Date(),
|
||||
type: 'text'
|
||||
};
|
||||
|
||||
this.debugLogger.log('HTTP', `Storing and broadcasting user message to ${this.sseConnections.size} SSE connections`);
|
||||
this.storeMessage(pendingRequest.params.sessionId, userMessage);
|
||||
this.broadcastMessageToClients(pendingRequest.params.sessionId, userMessage);
|
||||
|
||||
// Remove from ChatManager as well
|
||||
this.chatManager.removePendingRequest(pendingRequest.params.sessionId, requestId);
|
||||
this.debugLogger.log('HTTP', 'Broadcast completed and pending request removed from ChatManager');
|
||||
} else {
|
||||
this.debugLogger.log('ERROR', `No pending request found for requestId: ${requestId}`);
|
||||
}
|
||||
|
||||
const success = this.respondToHumanRequest(requestId, response);
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
@@ -680,6 +858,519 @@ export class McpServer extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleWebInterface(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
||||
this.debugLogger.log('HTTP', 'Serving web interface at /HumanAgent');
|
||||
|
||||
// Set HTML content type
|
||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
res.statusCode = 200;
|
||||
|
||||
const htmlContent = this.generateWebInterfaceHTML();
|
||||
res.end(htmlContent);
|
||||
}
|
||||
|
||||
private generateWebInterfaceHTML(): string {
|
||||
// Get all active sessions for tab generation
|
||||
const sessions = Array.from(this.activeSessions).map((sessionId) => {
|
||||
const workspaceRoot = this.sessionWorkspacePaths.get(sessionId);
|
||||
const friendlyName = this.sessionNames.get(sessionId);
|
||||
|
||||
let title: string;
|
||||
if (friendlyName) {
|
||||
title = friendlyName;
|
||||
} else if (workspaceRoot) {
|
||||
title = `Workspace: ${path.basename(workspaceRoot)}`;
|
||||
} else {
|
||||
title = `Session: ${sessionId.substring(0, 8)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
id: sessionId,
|
||||
title: title,
|
||||
messages: [] // TODO: Add proper message storage
|
||||
};
|
||||
});
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>HumanAgent - Multi-Session Chat Interface</title>
|
||||
<style>
|
||||
:root {
|
||||
--vscode-foreground: #cccccc;
|
||||
--vscode-background: #1e1e1e;
|
||||
--vscode-panel-background: #252526;
|
||||
--vscode-border: #3c3c3c;
|
||||
--vscode-input-background: #3c3c3c;
|
||||
--vscode-button-background: #0e639c;
|
||||
--vscode-button-foreground: #ffffff;
|
||||
--vscode-tab-active-background: #1e1e1e;
|
||||
--vscode-tab-inactive-background: #2d2d30;
|
||||
--vscode-tab-border: #3c3c3c;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
font-size: 13px;
|
||||
background-color: var(--vscode-background);
|
||||
color: var(--vscode-foreground);
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
padding: 10px 15px;
|
||||
background-color: var(--vscode-panel-background);
|
||||
border-bottom: 1px solid var(--vscode-border);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
display: flex;
|
||||
background-color: var(--vscode-panel-background);
|
||||
border-bottom: 1px solid var(--vscode-border);
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 8px 16px;
|
||||
background-color: var(--vscode-tab-inactive-background);
|
||||
border-right: 1px solid var(--vscode-tab-border);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
background-color: var(--vscode-tab-active-background);
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background-color: var(--vscode-tab-active-background);
|
||||
border-bottom: 2px solid var(--vscode-button-background);
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
flex: 1;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.chat-container.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
background-color: var(--vscode-background);
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.message.user {
|
||||
background-color: var(--vscode-input-background);
|
||||
margin-left: 20%;
|
||||
}
|
||||
|
||||
.message.assistant {
|
||||
background-color: var(--vscode-panel-background);
|
||||
margin-right: 20%;
|
||||
}
|
||||
|
||||
.message-header {
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
font-size: 11px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
padding: 15px;
|
||||
background-color: var(--vscode-panel-background);
|
||||
border-top: 1px solid var(--vscode-border);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
background-color: var(--vscode-input-background);
|
||||
border: 1px solid var(--vscode-border);
|
||||
border-radius: 4px;
|
||||
color: var(--vscode-foreground);
|
||||
font-size: 13px;
|
||||
resize: none;
|
||||
min-height: 36px;
|
||||
max-height: 120px;
|
||||
}
|
||||
|
||||
.send-button {
|
||||
padding: 8px 16px;
|
||||
background-color: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.send-button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.send-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.no-sessions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #4CAF50;
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1><span class="status-indicator"></span>HumanAgent Multi-Session Chat</h1>
|
||||
</div>
|
||||
|
||||
<div class="tabs-container" id="tabs">
|
||||
${sessions.length === 0 ? '' : sessions.map((session, index) =>
|
||||
`<div class="tab ${index === 0 ? 'active' : ''}" data-session="${session.id}">${session.title}</div>`
|
||||
).join('')}
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
${sessions.length === 0 ?
|
||||
'<div class="no-sessions">No active sessions. Start a chat in VS Code to see sessions here.</div>' :
|
||||
sessions.map((session, index) => `
|
||||
<div class="chat-container ${index === 0 ? 'active' : ''}" data-session="${session.id}">
|
||||
<div class="messages" id="messages-${session.id}">
|
||||
<!-- Messages will be loaded dynamically -->
|
||||
</div>
|
||||
<div class="input-container">
|
||||
<textarea class="input-box" placeholder="Type your message..." data-session="${session.id}"></textarea>
|
||||
<button class="send-button" data-session="${session.id}">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Session management
|
||||
let activeSessionId = '${sessions[0]?.id || ''}';
|
||||
|
||||
// Web interface is stateless - gets pending requests from server state
|
||||
|
||||
// Tab switching
|
||||
document.getElementById('tabs').addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('tab')) {
|
||||
const sessionId = e.target.dataset.session;
|
||||
switchToSession(sessionId);
|
||||
}
|
||||
});
|
||||
|
||||
function switchToSession(sessionId) {
|
||||
// Update active tab
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.classList.toggle('active', tab.dataset.session === sessionId);
|
||||
});
|
||||
|
||||
// Update active chat container
|
||||
document.querySelectorAll('.chat-container').forEach(container => {
|
||||
container.classList.toggle('active', container.dataset.session === sessionId);
|
||||
});
|
||||
|
||||
activeSessionId = sessionId;
|
||||
}
|
||||
|
||||
// Message sending
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('send-button')) {
|
||||
const sessionId = e.target.dataset.session;
|
||||
const textarea = document.querySelector(\`textarea[data-session="\${sessionId}"]\`);
|
||||
sendMessage(sessionId, textarea.value.trim());
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.target.classList.contains('input-box') && e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
const sessionId = e.target.dataset.session;
|
||||
sendMessage(sessionId, e.target.value.trim());
|
||||
}
|
||||
});
|
||||
|
||||
async function sendMessage(sessionId, message) {
|
||||
if (!message) return;
|
||||
|
||||
const textarea = document.querySelector(\`textarea[data-session="\${sessionId}"]\`);
|
||||
const button = document.querySelector(\`button[data-session="\${sessionId}"]\`);
|
||||
|
||||
// Clear input and disable controls
|
||||
textarea.value = '';
|
||||
textarea.disabled = true;
|
||||
button.disabled = true;
|
||||
|
||||
try {
|
||||
// Get current session state to find pending request
|
||||
const stateResponse = await fetch(\`/sessions/\${sessionId}/state\`);
|
||||
if (!stateResponse.ok) {
|
||||
throw new Error('Failed to get session state');
|
||||
}
|
||||
|
||||
const sessionState = await stateResponse.json();
|
||||
const latestPendingRequest = sessionState.latestPendingRequest;
|
||||
|
||||
if (!latestPendingRequest) {
|
||||
throw new Error('No pending AI request found. Web interface can only respond to AI questions.');
|
||||
}
|
||||
|
||||
console.log('Responding to pending request:', latestPendingRequest.requestId);
|
||||
|
||||
// Always use /response endpoint - web interface is response-only
|
||||
const response = await fetch('/response', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
requestId: latestPendingRequest.requestId,
|
||||
response: message
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(\`HTTP \${response.status}: \${response.statusText}\`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
console.log('Response sent successfully:', result);
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to send response');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to send response:', error);
|
||||
addMessageToUI(sessionId, 'assistant', \`Error: \${error.message}\`);
|
||||
} finally {
|
||||
// Re-enable controls
|
||||
textarea.disabled = false;
|
||||
button.disabled = false;
|
||||
textarea.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function addMessageToUI(sessionId, role, content) {
|
||||
const messagesContainer = document.getElementById(\`messages-\${sessionId}\`);
|
||||
if (!messagesContainer) return;
|
||||
|
||||
const messageDiv = document.createElement('div');
|
||||
messageDiv.className = \`message \${role}\`;
|
||||
messageDiv.innerHTML = \`
|
||||
<div class="message-header">\${role === 'user' ? 'You' : 'Assistant'} • \${new Date().toLocaleTimeString()}</div>
|
||||
<div class="message-content">\${escapeHtml(content)}</div>
|
||||
\`;
|
||||
|
||||
messagesContainer.appendChild(messageDiv);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
// Load existing messages for all sessions
|
||||
async function loadExistingMessages() {
|
||||
const sessions = ['${sessions.map(s => s.id).join("', '")}'];
|
||||
|
||||
for (const sessionId of sessions) {
|
||||
try {
|
||||
const response = await fetch(\`/messages/\${sessionId}\`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const messagesContainer = document.getElementById(\`messages-\${sessionId}\`);
|
||||
if (messagesContainer && data.messages) {
|
||||
// Clear any placeholder content
|
||||
messagesContainer.innerHTML = '';
|
||||
|
||||
// Add each message
|
||||
for (const msg of data.messages) {
|
||||
addMessageToUI(sessionId, msg.sender, msg.content);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(\`Failed to load messages for session \${sessionId}:\`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load conversation history from centralized chat manager
|
||||
async function loadConversationHistory() {
|
||||
const sessions = [${sessions.map(s => `'${s.id}'`).join(', ')}];
|
||||
|
||||
for (const sessionId of sessions) {
|
||||
try {
|
||||
console.log(\`Loading conversation history for session: \${sessionId}\`);
|
||||
|
||||
// Get messages from centralized chat manager
|
||||
const response = await fetch(\`/sessions/\${sessionId}/messages\`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const messagesContainer = document.getElementById(\`messages-\${sessionId}\`);
|
||||
|
||||
if (messagesContainer && data.messages) {
|
||||
// Clear any existing content
|
||||
messagesContainer.innerHTML = '';
|
||||
|
||||
// Add each message from chat manager
|
||||
for (const msg of data.messages) {
|
||||
addMessageToUI(sessionId, msg.sender, msg.content);
|
||||
}
|
||||
|
||||
console.log(\`Loaded \${data.messages.length} messages for session \${sessionId}\`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(\`Failed to load conversation history for session \${sessionId}:\`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WebSocket connection for real-time updates
|
||||
function setupRealtimeUpdates() {
|
||||
console.log('Setting up SSE connection to /mcp...');
|
||||
const eventSource = new EventSource('/mcp');
|
||||
|
||||
eventSource.onopen = function(event) {
|
||||
console.log('SSE connection opened successfully:', event);
|
||||
// Load conversation history for all sessions
|
||||
loadConversationHistory();
|
||||
};
|
||||
|
||||
eventSource.onmessage = function(event) {
|
||||
try {
|
||||
console.log('SSE message received:', event.data);
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('Real-time update:', data);
|
||||
|
||||
// Handle different types of updates
|
||||
if (data.type === 'chat_message' && data.sessionId && data.message) {
|
||||
addMessageToUI(data.sessionId, data.message.sender, data.message.content);
|
||||
} else if (data.type === 'message' && data.sessionId) {
|
||||
addMessageToUI(data.sessionId, data.role || 'assistant', data.content);
|
||||
} else if (data.type === 'human-agent-request' && data.data) {
|
||||
// Handle human-agent-request messages (AI questions to user)
|
||||
console.log('Web interface received human-agent-request:', data.data);
|
||||
|
||||
// Add AI message to current session (no need to track requestId - we get it from server state)
|
||||
const sessionId = activeSessionId || 'default';
|
||||
const displayMessage = data.data.context ? \`\${data.data.context}\\n\\n\${data.data.message}\` : data.data.message;
|
||||
addMessageToUI(sessionId, 'agent', displayMessage);
|
||||
} else if (data.type === 'session_update') {
|
||||
// Refresh the page to show new sessions
|
||||
window.location.reload();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to parse SSE message:', error);
|
||||
}
|
||||
};
|
||||
|
||||
eventSource.onerror = function(error) {
|
||||
console.error('SSE connection error:', error);
|
||||
console.error('EventSource readyState:', eventSource.readyState);
|
||||
console.error('EventSource url:', eventSource.url);
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize everything when page loads
|
||||
async function initialize() {
|
||||
await loadExistingMessages();
|
||||
setupRealtimeUpdates();
|
||||
|
||||
// Focus on input for active session
|
||||
if (activeSessionId) {
|
||||
const activeInput = document.querySelector(\`textarea[data-session="\${activeSessionId}"]\`);
|
||||
if (activeInput) activeInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Start initialization
|
||||
initialize();
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
private escapeHtml(text: string): string {
|
||||
const map: { [key: string]: string } = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
};
|
||||
return text.replace(/[&<>"']/g, (m) => map[m]);
|
||||
}
|
||||
|
||||
private handleInitialize(message: McpMessage): McpMessage {
|
||||
return {
|
||||
id: message.id,
|
||||
@@ -729,9 +1420,11 @@ export class McpServer extends EventEmitter {
|
||||
|
||||
private async handleToolCall(message: McpMessage): Promise<McpMessage> {
|
||||
const { name, arguments: args } = message.params;
|
||||
const sessionId = args?.sessionId; // Extract session ID from tool arguments
|
||||
// Extract session ID from _meta.vscode.conversationId (VS Code) or fallback to args.sessionId (web)
|
||||
const rawSessionId = message.params._meta?.['vscode.conversationId'] || args?.sessionId;
|
||||
const sessionId = this.mapToRegisteredSession(rawSessionId);
|
||||
|
||||
this.debugLogger.log('MCP', `Tool call - name: "${name}", session: ${sessionId}`, { name, args });
|
||||
this.debugLogger.log('MCP', `Tool call - name: "${name}", raw session: ${rawSessionId}, mapped session: ${sessionId}`, { name, args });
|
||||
|
||||
// Check session-specific tools first, then default tools
|
||||
const sessionTools = sessionId ? this.sessionTools.get(sessionId) : null;
|
||||
@@ -741,7 +1434,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);
|
||||
return await this.handleHumanAgentChatTool(message.id, args, sessionId);
|
||||
}
|
||||
|
||||
this.debugLogger.log('MCP', `Tool not found: ${name}`);
|
||||
@@ -755,7 +1448,7 @@ export class McpServer extends EventEmitter {
|
||||
};
|
||||
}
|
||||
|
||||
private async handleHumanAgentChatTool(messageId: string, params: HumanAgentChatToolParams): Promise<McpMessage> {
|
||||
private async handleHumanAgentChatTool(messageId: string, params: HumanAgentChatToolParams, sessionId?: 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
|
||||
@@ -794,13 +1487,29 @@ export class McpServer extends EventEmitter {
|
||||
});
|
||||
}, timeout);
|
||||
|
||||
// Store the pending request
|
||||
// Store the pending request using the extracted session ID
|
||||
const sessionToUse = sessionId || params.sessionId || 'default';
|
||||
this.debugLogger.log('TOOL', `Adding pending request ${requestId} to session: ${sessionToUse}`);
|
||||
this.chatManager.addPendingRequest(sessionToUse, requestId, params);
|
||||
this.pendingHumanRequests.set(requestId, {
|
||||
resolve: (response: string) => {
|
||||
clearTimeout(timeoutHandle);
|
||||
const responseTime = Date.now() - startTime;
|
||||
this.debugLogger.log('TOOL', `Request ${requestId} completed with response:`, response);
|
||||
|
||||
// Store the assistant response message for synchronization
|
||||
if (params.sessionId) {
|
||||
const assistantMessage: ChatMessage = {
|
||||
id: (Date.now() + 1).toString(), // Slightly different timestamp
|
||||
content: response,
|
||||
sender: 'agent',
|
||||
timestamp: new Date(),
|
||||
type: 'text'
|
||||
};
|
||||
this.storeMessage(params.sessionId, assistantMessage);
|
||||
this.broadcastMessageToClients(params.sessionId, assistantMessage);
|
||||
}
|
||||
|
||||
const result: HumanAgentChatToolResult = {
|
||||
content: [{
|
||||
type: 'text',
|
||||
@@ -871,7 +1580,14 @@ export class McpServer extends EventEmitter {
|
||||
resolvePendingRequest(requestId: string, response: string): boolean {
|
||||
const request = this.pendingHumanRequests.get(requestId);
|
||||
if (request) {
|
||||
// Remove from both places
|
||||
this.pendingHumanRequests.delete(requestId);
|
||||
// Find the session ID for this request and remove it from ChatManager
|
||||
for (const sessionId of this.chatManager.getActiveSessions()) {
|
||||
if (this.chatManager.removePendingRequest(sessionId, requestId)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
request.resolve(response);
|
||||
return true;
|
||||
}
|
||||
@@ -903,6 +1619,37 @@ export class McpServer extends EventEmitter {
|
||||
this.debugLogger.log('INFO', `Session registered: ${sessionId} (${this.activeSessions.size} total sessions)`);
|
||||
}
|
||||
|
||||
private mapToRegisteredSession(conversationId?: string): string | undefined {
|
||||
if (!conversationId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Check if it's already a registered session ID
|
||||
if (this.activeSessions.has(conversationId)) {
|
||||
return conversationId;
|
||||
}
|
||||
|
||||
// Check if it's a conversation ID we've seen before
|
||||
const mappedSession = this.conversationToSession.get(conversationId);
|
||||
if (mappedSession && this.activeSessions.has(mappedSession)) {
|
||||
this.debugLogger.log('MCP', `Mapped conversation ${conversationId} to session ${mappedSession}`);
|
||||
return mappedSession;
|
||||
}
|
||||
|
||||
// If not found, try to map to the first available registered session
|
||||
// This handles the case where VS Code conversation ID needs to be linked to a web-registered session
|
||||
const activeSessions = Array.from(this.activeSessions);
|
||||
if (activeSessions.length > 0) {
|
||||
const targetSession = activeSessions[0]; // Use first available session
|
||||
this.conversationToSession.set(conversationId, targetSession);
|
||||
this.debugLogger.log('MCP', `Auto-mapped conversation ${conversationId} to session ${targetSession}`);
|
||||
return targetSession;
|
||||
}
|
||||
|
||||
this.debugLogger.log('MCP', `No registered session found for conversation ${conversationId}`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
unregisterSession(sessionId: string): void {
|
||||
this.activeSessions.delete(sessionId);
|
||||
// Clean up session-specific data
|
||||
@@ -963,4 +1710,42 @@ export class McpServer extends EventEmitter {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Message storage and synchronization methods - now using ChatManager
|
||||
private storeMessage(sessionId: string, message: ChatMessage): void {
|
||||
this.chatManager.addMessage(sessionId, message);
|
||||
this.debugLogger.log('CHAT', `Stored message in ChatManager for session ${sessionId}: ${message.sender} - ${message.content.substring(0, 50)}...`);
|
||||
}
|
||||
|
||||
private getSessionMessages(sessionId: string): ChatMessage[] {
|
||||
return this.chatManager.getMessages(sessionId);
|
||||
}
|
||||
|
||||
private broadcastMessageToClients(sessionId: string, message: ChatMessage): void {
|
||||
// Broadcast message to all connected SSE clients
|
||||
const messageEvent = {
|
||||
type: 'chat_message',
|
||||
sessionId: sessionId,
|
||||
message: {
|
||||
id: message.id,
|
||||
content: message.content,
|
||||
sender: message.sender,
|
||||
timestamp: message.timestamp.toISOString()
|
||||
}
|
||||
};
|
||||
|
||||
const sseData = `data: ${JSON.stringify(messageEvent)}\n\n`;
|
||||
|
||||
// Send to all connected SSE clients (VS Code webviews and web interfaces)
|
||||
for (const connection of this.sseConnections) {
|
||||
try {
|
||||
connection.write(sseData);
|
||||
this.debugLogger.log('CHAT', `Broadcasted message to SSE client for session ${sessionId}`);
|
||||
} catch (error) {
|
||||
this.debugLogger.log('ERROR', 'Failed to broadcast message to SSE client:', error);
|
||||
// Remove failed connection
|
||||
this.sseConnections.delete(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
-56
@@ -15,7 +15,7 @@ export class ServerManager {
|
||||
private static instance: ServerManager | undefined;
|
||||
private options: ServerManagerOptions;
|
||||
private readonly pidFile: string;
|
||||
private readonly logFile: string;
|
||||
private readonly logFile?: string;
|
||||
|
||||
private constructor(options: ServerManagerOptions) {
|
||||
this.options = {
|
||||
@@ -23,7 +23,7 @@ export class ServerManager {
|
||||
...options
|
||||
};
|
||||
this.pidFile = path.join(path.dirname(options.serverPath), '.humanagent-mcp-server.pid');
|
||||
this.logFile = options.logFile || path.join(path.dirname(options.serverPath), '.humanagent-mcp-server.log');
|
||||
this.logFile = options.logFile;
|
||||
}
|
||||
|
||||
public static getInstance(options?: ServerManagerOptions): ServerManager {
|
||||
@@ -122,10 +122,12 @@ export class ServerManager {
|
||||
const timestamp = new Date().toISOString();
|
||||
const logMessage = `[${timestamp}] ${message}\n`;
|
||||
|
||||
try {
|
||||
fs.appendFileSync(this.logFile, logMessage);
|
||||
} catch (error) {
|
||||
console.error('Error writing to log file:', error);
|
||||
if (this.logFile) {
|
||||
try {
|
||||
fs.appendFileSync(this.logFile, logMessage);
|
||||
} catch (error) {
|
||||
console.error('Error writing to log file:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,59 +172,40 @@ export class ServerManager {
|
||||
* Start the server as a truly independent detached process
|
||||
*/
|
||||
private async startServer(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
try {
|
||||
// Spawn the server as a completely detached process
|
||||
const serverProcess = spawn('node', [this.options.serverPath], {
|
||||
detached: true,
|
||||
stdio: 'ignore', // Completely disconnect stdio to make it independent
|
||||
cwd: path.dirname(this.options.serverPath),
|
||||
env: {
|
||||
...process.env,
|
||||
// Add any environment variables needed by the server
|
||||
}
|
||||
});
|
||||
|
||||
// Store the PID but don't keep a reference to the process
|
||||
if (serverProcess.pid) {
|
||||
this.storePid(serverProcess.pid);
|
||||
this.log(`Started independent server with PID ${serverProcess.pid}`);
|
||||
try {
|
||||
// Spawn the server as a completely detached process
|
||||
const serverProcess = spawn('node', [this.options.serverPath], {
|
||||
detached: true,
|
||||
stdio: 'ignore', // Completely disconnect stdio to make it independent
|
||||
cwd: path.dirname(this.options.serverPath),
|
||||
env: {
|
||||
...process.env,
|
||||
// Add any environment variables needed by the server
|
||||
}
|
||||
});
|
||||
|
||||
// Immediately detach and unreference the process
|
||||
serverProcess.unref();
|
||||
|
||||
// Don't keep a reference to the process object to ensure complete detachment
|
||||
// this.serverProcess = undefined;
|
||||
|
||||
this.log('Server process started as independent background process');
|
||||
|
||||
// Wait for server to start listening
|
||||
setTimeout(async () => {
|
||||
if (await this.isServerRunning()) {
|
||||
this.log('Independent server started successfully and is responding');
|
||||
resolve(true);
|
||||
} else {
|
||||
this.log('Independent server started but is not responding on the expected port yet');
|
||||
// Give it a bit more time
|
||||
setTimeout(async () => {
|
||||
if (await this.isServerRunning()) {
|
||||
this.log('Independent server is now responding');
|
||||
resolve(true);
|
||||
} else {
|
||||
this.log('Independent server failed to start or is not responding');
|
||||
this.cleanupPidFile();
|
||||
resolve(false);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
} catch (error) {
|
||||
this.log(`Failed to start server: ${error}`);
|
||||
resolve(false);
|
||||
// Store the PID but don't keep a reference to the process
|
||||
if (serverProcess.pid) {
|
||||
this.storePid(serverProcess.pid);
|
||||
this.log(`Started independent server with PID ${serverProcess.pid}`);
|
||||
} else {
|
||||
this.log('Failed to get server process PID');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Immediately detach and unreference the process for complete independence
|
||||
serverProcess.unref();
|
||||
|
||||
this.log('Server process started as independent background process and immediately detached');
|
||||
|
||||
// Return true immediately - the server will start independently
|
||||
// Actual server health will be verified by separate health checks later
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
this.log(`Failed to start server: ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,28 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
this.loadNotificationSettings();
|
||||
}
|
||||
|
||||
private async loadConversationHistory() {
|
||||
if (!this.mcpServer || !this.workspaceSessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load conversation history:', error);
|
||||
// Fallback to empty message array
|
||||
this.messages = [];
|
||||
}
|
||||
}
|
||||
|
||||
private loadNotificationSettings() {
|
||||
try {
|
||||
// Try to load settings from mcp.json
|
||||
@@ -127,6 +149,9 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
]
|
||||
};
|
||||
|
||||
// Load conversation history from centralized chat manager
|
||||
this.loadConversationHistory();
|
||||
|
||||
// Only update webview if registration check is complete, otherwise it will be updated when notifyRegistrationComplete is called
|
||||
if (this.registrationCheckComplete) {
|
||||
this.updateWebview();
|
||||
@@ -144,6 +169,12 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
// Call the dedicated status command from extension.ts
|
||||
vscode.commands.executeCommand('humanagent-mcp.showStatus');
|
||||
break;
|
||||
case 'playNotificationSound':
|
||||
// Play sound from extension side (Node.js) when webview requests it
|
||||
if (this.notificationSettings.enableSound) {
|
||||
await this.playNotificationSound();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -448,6 +479,12 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
case 'reloadOverride':
|
||||
await this.reloadOverrideFile();
|
||||
break;
|
||||
case 'nameSession':
|
||||
await this.nameCurrentSession();
|
||||
break;
|
||||
case 'openWebView':
|
||||
await this.openWebInterface();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update status after action
|
||||
@@ -457,6 +494,72 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private async nameCurrentSession() {
|
||||
try {
|
||||
const sessionId = this.workspaceSessionId;
|
||||
if (!sessionId) {
|
||||
vscode.window.showErrorMessage('No active session to name.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prompt user for session name
|
||||
const sessionName = await vscode.window.showInputBox({
|
||||
prompt: 'Enter a friendly name for this chat session',
|
||||
placeHolder: 'e.g., "Project Debugging", "Feature Discussion"',
|
||||
validateInput: (text) => {
|
||||
if (!text || text.trim().length === 0) {
|
||||
return 'Session name cannot be empty';
|
||||
}
|
||||
if (text.length > 50) {
|
||||
return 'Session name must be 50 characters or less';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
if (!sessionName) {
|
||||
return; // User cancelled
|
||||
}
|
||||
|
||||
// Send session name to server
|
||||
const response = await fetch('http://localhost:3737/sessions/name', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
sessionId: sessionId,
|
||||
name: sessionName.trim()
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
vscode.window.showInformationMessage(`Session named: "${sessionName}"`);
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('ChatWebviewProvider: Error naming session:', error);
|
||||
vscode.window.showErrorMessage(`Failed to name session: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async openWebInterface() {
|
||||
try {
|
||||
const webUrl = 'http://localhost:3737/HumanAgent';
|
||||
|
||||
// Open in external browser
|
||||
await vscode.env.openExternal(vscode.Uri.parse(webUrl));
|
||||
|
||||
vscode.window.showInformationMessage('Web interface opened in browser');
|
||||
|
||||
} catch (error) {
|
||||
console.error('ChatWebviewProvider: Error opening web interface:', error);
|
||||
vscode.window.showErrorMessage(`Failed to open web interface: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
private _getHtmlForWebview(webview: vscode.Webview) {
|
||||
// Check if HumanAgentOverride.json exists in workspace
|
||||
let overrideFileExists = false;
|
||||
@@ -707,97 +810,19 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
// Set global flag for override file existence
|
||||
window.overrideFileExists = ${overrideFileExists};
|
||||
|
||||
// Audio context for notifications
|
||||
let audioContext = null;
|
||||
let preloadedAudio = null;
|
||||
|
||||
// Initialize audio on first user interaction
|
||||
function initAudio() {
|
||||
if (!audioContext) {
|
||||
try {
|
||||
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
// Resume in case it's in suspended state
|
||||
if (audioContext.state === 'suspended') {
|
||||
audioContext.resume();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to create audio context:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-load and test audio
|
||||
if (!preloadedAudio) {
|
||||
try {
|
||||
preloadedAudio = new Audio();
|
||||
preloadedAudio.src = "data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmMaBSGI0PTVnhY=";
|
||||
preloadedAudio.volume = 0.3;
|
||||
preloadedAudio.preload = 'auto';
|
||||
// Play and immediately pause to establish permission
|
||||
preloadedAudio.play().then(() => {
|
||||
preloadedAudio.pause();
|
||||
preloadedAudio.currentTime = 0;
|
||||
console.log('Audio initialized successfully');
|
||||
}).catch(e => {
|
||||
console.log('Audio initialization failed:', e);
|
||||
preloadedAudio = null;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to create audio element:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Play notification beep sound
|
||||
function playNotificationBeep() {
|
||||
// Request sound from extension (Node.js side) instead of browser
|
||||
try {
|
||||
if (preloadedAudio) {
|
||||
console.log('Playing preloaded audio');
|
||||
preloadedAudio.currentTime = 0;
|
||||
preloadedAudio.play().then(() => {
|
||||
console.log('Audio played successfully');
|
||||
}).catch(e => {
|
||||
console.log('Preloaded audio play failed:', e);
|
||||
// Try to re-initialize if failed
|
||||
initAudio();
|
||||
});
|
||||
} else {
|
||||
console.log('Audio not initialized - trying to initialize now');
|
||||
initAudio();
|
||||
// Try Web Audio API fallback
|
||||
try {
|
||||
if (audioContext && audioContext.state === 'running') {
|
||||
const oscillator = audioContext.createOscillator();
|
||||
const gainNode = audioContext.createGain();
|
||||
|
||||
oscillator.connect(gainNode);
|
||||
gainNode.connect(audioContext.destination);
|
||||
|
||||
oscillator.frequency.setValueAtTime(800, audioContext.currentTime);
|
||||
|
||||
gainNode.gain.setValueAtTime(0, audioContext.currentTime);
|
||||
gainNode.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.01);
|
||||
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
|
||||
|
||||
oscillator.start(audioContext.currentTime);
|
||||
oscillator.stop(audioContext.currentTime + 0.2);
|
||||
console.log('Web Audio fallback played');
|
||||
} else {
|
||||
console.log('Web Audio context not available');
|
||||
}
|
||||
} catch (e2) {
|
||||
console.error('Fallback audio also failed:', e2);
|
||||
}
|
||||
}
|
||||
vscode.postMessage({
|
||||
type: 'playNotificationSound'
|
||||
});
|
||||
console.log('Sound notification requested from extension');
|
||||
} catch (error) {
|
||||
console.error('Error playing notification sound:', error);
|
||||
console.error('Failed to request sound notification:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize audio on any user interaction
|
||||
document.addEventListener('click', initAudio, { once: true });
|
||||
document.addEventListener('keypress', initAudio, { once: true });
|
||||
document.addEventListener('touchstart', initAudio, { once: true });
|
||||
|
||||
document.getElementById('messageInput').addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
@@ -916,7 +941,9 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
{ text: '📦 Install Globally', action: 'register' },
|
||||
{ text: '📁 Install in Workspace', action: 'register' },
|
||||
{ text: '📊 Show Status', action: 'requestServerStatus' },
|
||||
{ text: window.overrideFileExists ? '📁 Recreate Override File' : '📁 Create Override File', action: 'overridePrompt' }
|
||||
{ text: window.overrideFileExists ? '📁 Recreate Override File' : '📁 Create Override File', action: 'overridePrompt' },
|
||||
{ text: '📝 Name This Chat', action: 'nameSession' },
|
||||
{ text: '🌐 Open Web View', action: 'openWebView' }
|
||||
];
|
||||
|
||||
// Check for override file existence even when status unknown
|
||||
@@ -943,6 +970,8 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
options.push({ text: '📊 Show Status', action: 'requestServerStatus' });
|
||||
options.push({ text: window.overrideFileExists ? '📁 Recreate Override File' : '📁 Create Override File', action: 'overridePrompt' });
|
||||
options.push({ text: '📝 Name This Chat', action: 'nameSession' });
|
||||
options.push({ text: '🌐 Open Web View', action: 'openWebView' });
|
||||
|
||||
// Check for HumanAgentOverride.json file existence (passed from extension)
|
||||
if (window.overrideFileExists) {
|
||||
@@ -1024,6 +1053,9 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
if (data.type === 'human-agent-request') {
|
||||
handleHumanAgentRequest(data.data);
|
||||
} else if (data.type === 'chat_message') {
|
||||
handleIncomingChatMessage(data);
|
||||
// Removed web_user_message auto-trigger - no longer needed
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing SSE data:', error);
|
||||
@@ -1103,6 +1135,49 @@ 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) {
|
||||
// Remove empty state if it exists
|
||||
const emptyState = messagesContainer.querySelector('.empty-state');
|
||||
if (emptyState) {
|
||||
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;
|
||||
|
||||
// Play notification for assistant messages
|
||||
if (message.sender === 'agent') {
|
||||
playNotificationBeep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleWebUserMessage removed - no longer needed for auto-forwarding
|
||||
|
||||
// Initialize SSE connection
|
||||
setupSSEConnection();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user