claude-mpm 3.7.4__py3-none-any.whl → 3.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/BASE_PM.md +0 -106
- claude_mpm/agents/INSTRUCTIONS.md +0 -78
- claude_mpm/agents/MEMORY.md +88 -0
- claude_mpm/agents/WORKFLOW.md +86 -0
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/templates/code_analyzer.json +26 -11
- claude_mpm/agents/templates/data_engineer.json +4 -7
- claude_mpm/agents/templates/documentation.json +2 -2
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +3 -8
- claude_mpm/agents/templates/qa.json +2 -3
- claude_mpm/agents/templates/research.json +2 -3
- claude_mpm/agents/templates/security.json +3 -6
- claude_mpm/agents/templates/ticketing.json +4 -9
- claude_mpm/agents/templates/version_control.json +3 -3
- claude_mpm/agents/templates/web_qa.json +4 -4
- claude_mpm/agents/templates/web_ui.json +4 -4
- claude_mpm/cli/__init__.py +2 -2
- claude_mpm/cli/commands/__init__.py +2 -1
- claude_mpm/cli/commands/agents.py +118 -1
- claude_mpm/cli/commands/tickets.py +596 -19
- claude_mpm/cli/parser.py +228 -5
- claude_mpm/config/__init__.py +30 -39
- claude_mpm/config/socketio_config.py +8 -5
- claude_mpm/constants.py +13 -0
- claude_mpm/core/__init__.py +8 -18
- claude_mpm/core/cache.py +596 -0
- claude_mpm/core/claude_runner.py +166 -622
- claude_mpm/core/config.py +5 -1
- claude_mpm/core/constants.py +339 -0
- claude_mpm/core/container.py +461 -22
- claude_mpm/core/exceptions.py +392 -0
- claude_mpm/core/framework_loader.py +208 -93
- claude_mpm/core/interactive_session.py +432 -0
- claude_mpm/core/interfaces.py +424 -0
- claude_mpm/core/lazy.py +467 -0
- claude_mpm/core/logging_config.py +444 -0
- claude_mpm/core/oneshot_session.py +465 -0
- claude_mpm/core/optimized_agent_loader.py +485 -0
- claude_mpm/core/optimized_startup.py +490 -0
- claude_mpm/core/service_registry.py +52 -26
- claude_mpm/core/socketio_pool.py +162 -5
- claude_mpm/core/types.py +292 -0
- claude_mpm/core/typing_utils.py +477 -0
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +46 -2
- claude_mpm/dashboard/templates/index.html +5 -5
- claude_mpm/hooks/claude_hooks/hook_handler.py +213 -99
- claude_mpm/init.py +2 -1
- claude_mpm/services/__init__.py +78 -14
- claude_mpm/services/agent/__init__.py +24 -0
- claude_mpm/services/agent/deployment.py +2548 -0
- claude_mpm/services/agent/management.py +598 -0
- claude_mpm/services/agent/registry.py +813 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +592 -269
- claude_mpm/services/agents/deployment/async_agent_deployment.py +5 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +21 -11
- claude_mpm/services/agents/memory/agent_memory_manager.py +156 -1
- claude_mpm/services/async_session_logger.py +8 -3
- claude_mpm/services/communication/__init__.py +21 -0
- claude_mpm/services/communication/socketio.py +1933 -0
- claude_mpm/services/communication/websocket.py +479 -0
- claude_mpm/services/core/__init__.py +123 -0
- claude_mpm/services/core/base.py +247 -0
- claude_mpm/services/core/interfaces.py +951 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +23 -23
- claude_mpm/services/framework_claude_md_generator.py +3 -2
- claude_mpm/services/health_monitor.py +4 -3
- claude_mpm/services/hook_service.py +64 -4
- claude_mpm/services/infrastructure/__init__.py +21 -0
- claude_mpm/services/infrastructure/logging.py +202 -0
- claude_mpm/services/infrastructure/monitoring.py +893 -0
- claude_mpm/services/memory/indexed_memory.py +648 -0
- claude_mpm/services/project/__init__.py +21 -0
- claude_mpm/services/project/analyzer.py +864 -0
- claude_mpm/services/project/registry.py +608 -0
- claude_mpm/services/project_analyzer.py +95 -2
- claude_mpm/services/recovery_manager.py +15 -9
- claude_mpm/services/socketio/__init__.py +25 -0
- claude_mpm/services/socketio/handlers/__init__.py +25 -0
- claude_mpm/services/socketio/handlers/base.py +121 -0
- claude_mpm/services/socketio/handlers/connection.py +198 -0
- claude_mpm/services/socketio/handlers/file.py +213 -0
- claude_mpm/services/socketio/handlers/git.py +723 -0
- claude_mpm/services/socketio/handlers/memory.py +27 -0
- claude_mpm/services/socketio/handlers/project.py +25 -0
- claude_mpm/services/socketio/handlers/registry.py +145 -0
- claude_mpm/services/socketio_client_manager.py +12 -7
- claude_mpm/services/socketio_server.py +156 -30
- claude_mpm/services/ticket_manager.py +377 -51
- claude_mpm/utils/agent_dependency_loader.py +66 -15
- claude_mpm/utils/error_handler.py +1 -1
- claude_mpm/utils/robust_installer.py +587 -0
- claude_mpm/validation/agent_validator.py +27 -14
- claude_mpm/validation/frontmatter_validator.py +231 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/METADATA +74 -41
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/RECORD +101 -76
- claude_mpm/.claude-mpm/logs/hooks_20250728.log +0 -10
- claude_mpm/agents/agent-template.yaml +0 -83
- claude_mpm/cli/README.md +0 -108
- claude_mpm/cli_module/refactoring_guide.md +0 -253
- claude_mpm/config/async_logging_config.yaml +0 -145
- claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +0 -34
- claude_mpm/dashboard/.claude-mpm/memories/README.md +0 -36
- claude_mpm/dashboard/README.md +0 -121
- claude_mpm/dashboard/static/js/dashboard.js.backup +0 -1973
- claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +0 -36
- claude_mpm/dashboard/templates/.claude-mpm/memories/engineer_agent.md +0 -39
- claude_mpm/dashboard/templates/.claude-mpm/memories/version_control_agent.md +0 -38
- claude_mpm/hooks/README.md +0 -96
- claude_mpm/schemas/agent_schema.json +0 -435
- claude_mpm/services/framework_claude_md_generator/README.md +0 -92
- claude_mpm/services/version_control/VERSION +0 -1
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/WHEEL +0 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/top_level.txt +0 -0
| @@ -0,0 +1,479 @@ | |
| 1 | 
            +
            """Socket.IO client manager for connecting to standalone servers.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This module provides intelligent client management that:
         | 
| 4 | 
            +
            1. Detects existing standalone servers
         | 
| 5 | 
            +
            2. Performs version compatibility checks
         | 
| 6 | 
            +
            3. Handles graceful fallback to embedded servers
         | 
| 7 | 
            +
            4. Manages connection lifecycle and reconnection
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            WHY this approach:
         | 
| 10 | 
            +
            - Enables seamless integration with standalone servers
         | 
| 11 | 
            +
            - Provides compatibility checking before connection
         | 
| 12 | 
            +
            - Handles both local and remote server scenarios
         | 
| 13 | 
            +
            - Maintains backward compatibility with embedded servers
         | 
| 14 | 
            +
            """
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            import asyncio
         | 
| 17 | 
            +
            import json
         | 
| 18 | 
            +
            import logging
         | 
| 19 | 
            +
            import socket
         | 
| 20 | 
            +
            import sys
         | 
| 21 | 
            +
            import threading
         | 
| 22 | 
            +
            import time
         | 
| 23 | 
            +
            from datetime import datetime
         | 
| 24 | 
            +
            from typing import Dict, Any, Optional, List, Tuple
         | 
| 25 | 
            +
            import importlib.metadata
         | 
| 26 | 
            +
            from claude_mpm.core.constants import (
         | 
| 27 | 
            +
                NetworkConfig,
         | 
| 28 | 
            +
                TimeoutConfig,
         | 
| 29 | 
            +
                PerformanceConfig
         | 
| 30 | 
            +
            )
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            try:
         | 
| 33 | 
            +
                import requests
         | 
| 34 | 
            +
                import socketio
         | 
| 35 | 
            +
                DEPENDENCIES_AVAILABLE = True
         | 
| 36 | 
            +
            except ImportError:
         | 
| 37 | 
            +
                DEPENDENCIES_AVAILABLE = False
         | 
| 38 | 
            +
                requests = None
         | 
| 39 | 
            +
                socketio = None
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            from ..core.logger import get_logger
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            # Get claude-mpm version for compatibility checking
         | 
| 44 | 
            +
            try:
         | 
| 45 | 
            +
                CLAUDE_MPM_VERSION = importlib.metadata.version('claude-mpm')
         | 
| 46 | 
            +
            except Exception:
         | 
| 47 | 
            +
                # Fallback for development
         | 
| 48 | 
            +
                CLAUDE_MPM_VERSION = "0.7.0-dev"
         | 
| 49 | 
            +
             | 
| 50 | 
            +
             | 
| 51 | 
            +
            class ServerInfo:
         | 
| 52 | 
            +
                """Information about a detected Socket.IO server."""
         | 
| 53 | 
            +
                
         | 
| 54 | 
            +
                def __init__(self, host: str, port: int, response_data: Dict[str, Any]):
         | 
| 55 | 
            +
                    self.host = host
         | 
| 56 | 
            +
                    self.port = port
         | 
| 57 | 
            +
                    self.server_version = response_data.get("server_version", "unknown")
         | 
| 58 | 
            +
                    self.server_id = response_data.get("server_id", "unknown")
         | 
| 59 | 
            +
                    self.socketio_version = response_data.get("socketio_version", "unknown")
         | 
| 60 | 
            +
                    self.features = response_data.get("features", [])
         | 
| 61 | 
            +
                    self.supported_client_versions = response_data.get("supported_client_versions", [])
         | 
| 62 | 
            +
                    self.compatibility_matrix = response_data.get("compatibility_matrix", {})
         | 
| 63 | 
            +
                    self.detected_at = datetime.utcnow()
         | 
| 64 | 
            +
                
         | 
| 65 | 
            +
                @property
         | 
| 66 | 
            +
                def url(self) -> str:
         | 
| 67 | 
            +
                    return f"http://{self.host}:{self.port}"
         | 
| 68 | 
            +
                
         | 
| 69 | 
            +
                def is_compatible(self, client_version: str = CLAUDE_MPM_VERSION) -> Tuple[bool, List[str]]:
         | 
| 70 | 
            +
                    """Check if this server is compatible with the client version."""
         | 
| 71 | 
            +
                    warnings = []
         | 
| 72 | 
            +
                    
         | 
| 73 | 
            +
                    try:
         | 
| 74 | 
            +
                        # Simple version comparison - in production use proper semver
         | 
| 75 | 
            +
                        if client_version >= "0.7.0":
         | 
| 76 | 
            +
                            return True, warnings
         | 
| 77 | 
            +
                        else:
         | 
| 78 | 
            +
                            warnings.append(f"Client version {client_version} may not be fully supported")
         | 
| 79 | 
            +
                            return False, warnings
         | 
| 80 | 
            +
                    except Exception as e:
         | 
| 81 | 
            +
                        warnings.append(f"Could not parse version: {e}")
         | 
| 82 | 
            +
                        return False, warnings
         | 
| 83 | 
            +
             | 
| 84 | 
            +
             | 
| 85 | 
            +
            class SocketIOClientManager:
         | 
| 86 | 
            +
                """Manages Socket.IO client connections with server discovery and compatibility checking."""
         | 
| 87 | 
            +
                
         | 
| 88 | 
            +
                def __init__(self, client_version: str = CLAUDE_MPM_VERSION):
         | 
| 89 | 
            +
                    self.client_version = client_version
         | 
| 90 | 
            +
                    self.logger = get_logger("socketio_client_manager")
         | 
| 91 | 
            +
                    
         | 
| 92 | 
            +
                    # Connection state
         | 
| 93 | 
            +
                    self.current_server: Optional[ServerInfo] = None
         | 
| 94 | 
            +
                    self.client: Optional[socketio.AsyncClient] = None
         | 
| 95 | 
            +
                    self.connected = False
         | 
| 96 | 
            +
                    self.connection_thread: Optional[threading.Thread] = None
         | 
| 97 | 
            +
                    self.running = False
         | 
| 98 | 
            +
                    
         | 
| 99 | 
            +
                    # Server discovery
         | 
| 100 | 
            +
                    self.known_servers: Dict[str, ServerInfo] = {}
         | 
| 101 | 
            +
                    self.last_discovery = None
         | 
| 102 | 
            +
                    
         | 
| 103 | 
            +
                    if not DEPENDENCIES_AVAILABLE:
         | 
| 104 | 
            +
                        self.logger.warning("Socket.IO client dependencies not available")
         | 
| 105 | 
            +
                
         | 
| 106 | 
            +
                def discover_servers(self, ports: List[int] = None, hosts: List[str] = None) -> List[ServerInfo]:
         | 
| 107 | 
            +
                    """Discover available Socket.IO servers.
         | 
| 108 | 
            +
                    
         | 
| 109 | 
            +
                    Args:
         | 
| 110 | 
            +
                        ports: List of ports to check (default: [8765, 8766, 8767])
         | 
| 111 | 
            +
                        hosts: List of hosts to check (default: ['localhost', '127.0.0.1'])
         | 
| 112 | 
            +
                    
         | 
| 113 | 
            +
                    Returns:
         | 
| 114 | 
            +
                        List of discovered server info objects
         | 
| 115 | 
            +
                    """
         | 
| 116 | 
            +
                    if not DEPENDENCIES_AVAILABLE:
         | 
| 117 | 
            +
                        return []
         | 
| 118 | 
            +
                    
         | 
| 119 | 
            +
                    ports = ports or [8765, 8766, 8767]
         | 
| 120 | 
            +
                    hosts = hosts or ['localhost', '127.0.0.1']
         | 
| 121 | 
            +
                    
         | 
| 122 | 
            +
                    discovered = []
         | 
| 123 | 
            +
                    
         | 
| 124 | 
            +
                    for host in hosts:
         | 
| 125 | 
            +
                        for port in ports:
         | 
| 126 | 
            +
                            try:
         | 
| 127 | 
            +
                                # Quick port check first
         | 
| 128 | 
            +
                                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
         | 
| 129 | 
            +
                                    s.settimeout(0.5)
         | 
| 130 | 
            +
                                    if s.connect_ex((host, port)) != 0:
         | 
| 131 | 
            +
                                        continue
         | 
| 132 | 
            +
                                
         | 
| 133 | 
            +
                                # Try to get server version info
         | 
| 134 | 
            +
                                try:
         | 
| 135 | 
            +
                                    response = requests.get(
         | 
| 136 | 
            +
                                        f"http://{host}:{port}/version",
         | 
| 137 | 
            +
                                        timeout=TimeoutConfig.QUICK_TIMEOUT
         | 
| 138 | 
            +
                                    )
         | 
| 139 | 
            +
                                    if response.status_code == 200:
         | 
| 140 | 
            +
                                        data = response.json()
         | 
| 141 | 
            +
                                        server_info = ServerInfo(host, port, data)
         | 
| 142 | 
            +
                                        discovered.append(server_info)
         | 
| 143 | 
            +
                                        
         | 
| 144 | 
            +
                                        server_key = f"{host}:{port}"
         | 
| 145 | 
            +
                                        self.known_servers[server_key] = server_info
         | 
| 146 | 
            +
                                        
         | 
| 147 | 
            +
                                        self.logger.info(f"🔍 Discovered server: {server_info.server_id} "
         | 
| 148 | 
            +
                                                       f"v{server_info.server_version} at {server_info.url}")
         | 
| 149 | 
            +
                                
         | 
| 150 | 
            +
                                except requests.RequestException:
         | 
| 151 | 
            +
                                    # Not a standalone server, might be another service
         | 
| 152 | 
            +
                                    continue
         | 
| 153 | 
            +
                                    
         | 
| 154 | 
            +
                            except Exception as e:
         | 
| 155 | 
            +
                                self.logger.debug(f"Error checking {host}:{port}: {e}")
         | 
| 156 | 
            +
                                continue
         | 
| 157 | 
            +
                    
         | 
| 158 | 
            +
                    self.last_discovery = datetime.utcnow()
         | 
| 159 | 
            +
                    return discovered
         | 
| 160 | 
            +
                
         | 
| 161 | 
            +
                def find_best_server(self, discovered_servers: List[ServerInfo] = None) -> Optional[ServerInfo]:
         | 
| 162 | 
            +
                    """Find the best compatible server from discovered servers."""
         | 
| 163 | 
            +
                    if discovered_servers is None:
         | 
| 164 | 
            +
                        discovered_servers = self.discover_servers()
         | 
| 165 | 
            +
                    
         | 
| 166 | 
            +
                    if not discovered_servers:
         | 
| 167 | 
            +
                        return None
         | 
| 168 | 
            +
                    
         | 
| 169 | 
            +
                    # Score servers based on compatibility and features
         | 
| 170 | 
            +
                    scored_servers = []
         | 
| 171 | 
            +
                    
         | 
| 172 | 
            +
                    for server in discovered_servers:
         | 
| 173 | 
            +
                        compatible, warnings = server.is_compatible(self.client_version)
         | 
| 174 | 
            +
                        
         | 
| 175 | 
            +
                        if not compatible:
         | 
| 176 | 
            +
                            self.logger.debug(f"Server {server.server_id} not compatible: {warnings}")
         | 
| 177 | 
            +
                            continue
         | 
| 178 | 
            +
                        
         | 
| 179 | 
            +
                        # Simple scoring: newer versions and more features get higher scores
         | 
| 180 | 
            +
                        score = 0
         | 
| 181 | 
            +
                        
         | 
| 182 | 
            +
                        # Version scoring (basic semantic versioning)
         | 
| 183 | 
            +
                        try:
         | 
| 184 | 
            +
                            version_parts = server.server_version.split('.')
         | 
| 185 | 
            +
                            score += int(version_parts[0]) * PerformanceConfig.VERSION_SCORE_MAJOR
         | 
| 186 | 
            +
                            score += int(version_parts[1]) * PerformanceConfig.VERSION_SCORE_MINOR
         | 
| 187 | 
            +
                            score += int(version_parts[2]) * PerformanceConfig.VERSION_SCORE_PATCH
         | 
| 188 | 
            +
                        except (ValueError, IndexError):
         | 
| 189 | 
            +
                            pass
         | 
| 190 | 
            +
                        
         | 
| 191 | 
            +
                        # Feature scoring
         | 
| 192 | 
            +
                        score += len(server.features) * 5
         | 
| 193 | 
            +
                        
         | 
| 194 | 
            +
                        scored_servers.append((score, server))
         | 
| 195 | 
            +
                    
         | 
| 196 | 
            +
                    if not scored_servers:
         | 
| 197 | 
            +
                        self.logger.warning("No compatible servers found")
         | 
| 198 | 
            +
                        return None
         | 
| 199 | 
            +
                    
         | 
| 200 | 
            +
                    # Return highest scored server
         | 
| 201 | 
            +
                    scored_servers.sort(key=lambda x: x[0], reverse=True)
         | 
| 202 | 
            +
                    best_server = scored_servers[0][1]
         | 
| 203 | 
            +
                    
         | 
| 204 | 
            +
                    self.logger.info(f"🎯 Selected best server: {best_server.server_id} "
         | 
| 205 | 
            +
                                    f"v{best_server.server_version} at {best_server.url}")
         | 
| 206 | 
            +
                    
         | 
| 207 | 
            +
                    return best_server
         | 
| 208 | 
            +
                
         | 
| 209 | 
            +
                async def connect_to_server(self, server_info: ServerInfo) -> bool:
         | 
| 210 | 
            +
                    """Connect to a specific server with compatibility verification."""
         | 
| 211 | 
            +
                    if not DEPENDENCIES_AVAILABLE:
         | 
| 212 | 
            +
                        return False
         | 
| 213 | 
            +
                    
         | 
| 214 | 
            +
                    try:
         | 
| 215 | 
            +
                        # Perform compatibility check via HTTP first
         | 
| 216 | 
            +
                        compat_response = requests.post(
         | 
| 217 | 
            +
                            f"{server_info.url}/compatibility",
         | 
| 218 | 
            +
                            json={"client_version": self.client_version},
         | 
| 219 | 
            +
                            timeout=TimeoutConfig.FILE_OPERATION_TIMEOUT
         | 
| 220 | 
            +
                        )
         | 
| 221 | 
            +
                        
         | 
| 222 | 
            +
                        if compat_response.status_code == 200:
         | 
| 223 | 
            +
                            compatibility = compat_response.json()
         | 
| 224 | 
            +
                            if not compatibility.get("compatible", False):
         | 
| 225 | 
            +
                                self.logger.error(f"Server {server_info.server_id} rejected client version {self.client_version}")
         | 
| 226 | 
            +
                                return False
         | 
| 227 | 
            +
                        
         | 
| 228 | 
            +
                        # Create Socket.IO client
         | 
| 229 | 
            +
                        self.client = socketio.AsyncClient(
         | 
| 230 | 
            +
                            reconnection=True,
         | 
| 231 | 
            +
                            reconnection_attempts=0,  # Infinite
         | 
| 232 | 
            +
                            reconnection_delay=NetworkConfig.RECONNECTION_DELAY,
         | 
| 233 | 
            +
                            reconnection_delay_max=5,
         | 
| 234 | 
            +
                            randomization_factor=0.5,
         | 
| 235 | 
            +
                            logger=False,
         | 
| 236 | 
            +
                            engineio_logger=False
         | 
| 237 | 
            +
                        )
         | 
| 238 | 
            +
                        
         | 
| 239 | 
            +
                        # Setup event handlers
         | 
| 240 | 
            +
                        self._setup_client_event_handlers()
         | 
| 241 | 
            +
                        
         | 
| 242 | 
            +
                        # Connect with authentication
         | 
| 243 | 
            +
                        auth = {
         | 
| 244 | 
            +
                            "claude_mpm_version": self.client_version,
         | 
| 245 | 
            +
                            "client_id": f"claude-mpm-{time.time()}"
         | 
| 246 | 
            +
                        }
         | 
| 247 | 
            +
                        
         | 
| 248 | 
            +
                        await self.client.connect(server_info.url, auth=auth)
         | 
| 249 | 
            +
                        
         | 
| 250 | 
            +
                        self.current_server = server_info
         | 
| 251 | 
            +
                        self.connected = True
         | 
| 252 | 
            +
                        
         | 
| 253 | 
            +
                        self.logger.info(f"✅ Connected to server {server_info.server_id} at {server_info.url}")
         | 
| 254 | 
            +
                        return True
         | 
| 255 | 
            +
                        
         | 
| 256 | 
            +
                    except Exception as e:
         | 
| 257 | 
            +
                        self.logger.error(f"❌ Failed to connect to server {server_info.url}: {e}")
         | 
| 258 | 
            +
                        if self.client:
         | 
| 259 | 
            +
                            try:
         | 
| 260 | 
            +
                                await self.client.disconnect()
         | 
| 261 | 
            +
                            except:
         | 
| 262 | 
            +
                                pass
         | 
| 263 | 
            +
                            self.client = None
         | 
| 264 | 
            +
                        return False
         | 
| 265 | 
            +
                
         | 
| 266 | 
            +
                def _setup_client_event_handlers(self):
         | 
| 267 | 
            +
                    """Setup event handlers for the Socket.IO client."""
         | 
| 268 | 
            +
                    
         | 
| 269 | 
            +
                    @self.client.event
         | 
| 270 | 
            +
                    async def connect():
         | 
| 271 | 
            +
                        self.logger.info("🔗 Socket.IO client connected")
         | 
| 272 | 
            +
                        self.connected = True
         | 
| 273 | 
            +
                    
         | 
| 274 | 
            +
                    @self.client.event
         | 
| 275 | 
            +
                    async def disconnect():
         | 
| 276 | 
            +
                        self.logger.info("🔌 Socket.IO client disconnected")
         | 
| 277 | 
            +
                        self.connected = False
         | 
| 278 | 
            +
                    
         | 
| 279 | 
            +
                    @self.client.event
         | 
| 280 | 
            +
                    async def connection_ack(data):
         | 
| 281 | 
            +
                        self.logger.info("🤝 Received connection acknowledgment")
         | 
| 282 | 
            +
                        compatibility = data.get("compatibility", {})
         | 
| 283 | 
            +
                        if not compatibility.get("compatible", True):
         | 
| 284 | 
            +
                            self.logger.warning(f"⚠️ Server compatibility warning: {compatibility.get('warnings', [])}")
         | 
| 285 | 
            +
                    
         | 
| 286 | 
            +
                    @self.client.event
         | 
| 287 | 
            +
                    async def compatibility_warning(data):
         | 
| 288 | 
            +
                        self.logger.warning(f"⚠️ Server compatibility warning: {data}")
         | 
| 289 | 
            +
                    
         | 
| 290 | 
            +
                    @self.client.event
         | 
| 291 | 
            +
                    async def server_status(data):
         | 
| 292 | 
            +
                        self.logger.debug(f"📊 Server status: {data.get('clients_connected', 0)} clients")
         | 
| 293 | 
            +
                    
         | 
| 294 | 
            +
                    @self.client.event
         | 
| 295 | 
            +
                    async def claude_event(data):
         | 
| 296 | 
            +
                        """Handle events broadcasted from other clients."""
         | 
| 297 | 
            +
                        event_type = data.get("type", "unknown")
         | 
| 298 | 
            +
                        self.logger.debug(f"📥 Received claude_event: {event_type}")
         | 
| 299 | 
            +
                
         | 
| 300 | 
            +
                async def emit_event(self, event_type: str, data: Dict[str, Any]) -> bool:
         | 
| 301 | 
            +
                    """Emit an event to the connected server."""
         | 
| 302 | 
            +
                    if not self.connected or not self.client:
         | 
| 303 | 
            +
                        return False
         | 
| 304 | 
            +
                    
         | 
| 305 | 
            +
                    try:
         | 
| 306 | 
            +
                        event_data = {
         | 
| 307 | 
            +
                            "type": event_type,
         | 
| 308 | 
            +
                            "timestamp": datetime.utcnow().isoformat() + "Z",
         | 
| 309 | 
            +
                            "data": data,
         | 
| 310 | 
            +
                            "client_version": self.client_version
         | 
| 311 | 
            +
                        }
         | 
| 312 | 
            +
                        
         | 
| 313 | 
            +
                        await self.client.emit('claude_event', event_data)
         | 
| 314 | 
            +
                        self.logger.debug(f"📤 Emitted event: {event_type}")
         | 
| 315 | 
            +
                        return True
         | 
| 316 | 
            +
                        
         | 
| 317 | 
            +
                    except Exception as e:
         | 
| 318 | 
            +
                        self.logger.error(f"❌ Failed to emit event {event_type}: {e}")
         | 
| 319 | 
            +
                        return False
         | 
| 320 | 
            +
                
         | 
| 321 | 
            +
                def start_connection_manager(self):
         | 
| 322 | 
            +
                    """Start the connection manager in a background thread."""
         | 
| 323 | 
            +
                    if self.running:
         | 
| 324 | 
            +
                        return
         | 
| 325 | 
            +
                    
         | 
| 326 | 
            +
                    self.running = True
         | 
| 327 | 
            +
                    self.connection_thread = threading.Thread(target=self._run_connection_manager, daemon=True)
         | 
| 328 | 
            +
                    self.connection_thread.start()
         | 
| 329 | 
            +
                    self.logger.info("🚀 Connection manager started")
         | 
| 330 | 
            +
                
         | 
| 331 | 
            +
                def stop_connection_manager(self):
         | 
| 332 | 
            +
                    """Stop the connection manager."""
         | 
| 333 | 
            +
                    self.running = False
         | 
| 334 | 
            +
                    
         | 
| 335 | 
            +
                    if self.connection_thread:
         | 
| 336 | 
            +
                        self.connection_thread.join(timeout=TimeoutConfig.THREAD_JOIN_TIMEOUT)
         | 
| 337 | 
            +
                    
         | 
| 338 | 
            +
                    if self.client and self.connected:
         | 
| 339 | 
            +
                        try:
         | 
| 340 | 
            +
                            # Disconnect from server
         | 
| 341 | 
            +
                            loop = asyncio.new_event_loop()
         | 
| 342 | 
            +
                            loop.run_until_complete(self.client.disconnect())
         | 
| 343 | 
            +
                            loop.close()
         | 
| 344 | 
            +
                        except Exception as e:
         | 
| 345 | 
            +
                            self.logger.error(f"Error disconnecting client: {e}")
         | 
| 346 | 
            +
                    
         | 
| 347 | 
            +
                    self.logger.info("🛑 Connection manager stopped")
         | 
| 348 | 
            +
                
         | 
| 349 | 
            +
                def _run_connection_manager(self):
         | 
| 350 | 
            +
                    """Run the connection manager loop."""
         | 
| 351 | 
            +
                    loop = asyncio.new_event_loop()
         | 
| 352 | 
            +
                    asyncio.set_event_loop(loop)
         | 
| 353 | 
            +
                    
         | 
| 354 | 
            +
                    try:
         | 
| 355 | 
            +
                        loop.run_until_complete(self._connection_manager_loop())
         | 
| 356 | 
            +
                    except Exception as e:
         | 
| 357 | 
            +
                        self.logger.error(f"Connection manager error: {e}")
         | 
| 358 | 
            +
                    finally:
         | 
| 359 | 
            +
                        loop.close()
         | 
| 360 | 
            +
                
         | 
| 361 | 
            +
                async def _connection_manager_loop(self):
         | 
| 362 | 
            +
                    """Main connection manager loop."""
         | 
| 363 | 
            +
                    connection_attempts = 0
         | 
| 364 | 
            +
                    max_connection_attempts = 3
         | 
| 365 | 
            +
                    
         | 
| 366 | 
            +
                    while self.running:
         | 
| 367 | 
            +
                        try:
         | 
| 368 | 
            +
                            if not self.connected:
         | 
| 369 | 
            +
                                if connection_attempts < max_connection_attempts:
         | 
| 370 | 
            +
                                    # Try to find and connect to a server
         | 
| 371 | 
            +
                                    best_server = self.find_best_server()
         | 
| 372 | 
            +
                                    
         | 
| 373 | 
            +
                                    if best_server:
         | 
| 374 | 
            +
                                        success = await self.connect_to_server(best_server)
         | 
| 375 | 
            +
                                        if success:
         | 
| 376 | 
            +
                                            connection_attempts = 0  # Reset on successful connection
         | 
| 377 | 
            +
                                        else:
         | 
| 378 | 
            +
                                            connection_attempts += 1
         | 
| 379 | 
            +
                                    else:
         | 
| 380 | 
            +
                                        self.logger.info("📡 No Socket.IO servers found, will retry...")
         | 
| 381 | 
            +
                                        connection_attempts += 1
         | 
| 382 | 
            +
                                else:
         | 
| 383 | 
            +
                                    # Too many failed attempts, wait longer
         | 
| 384 | 
            +
                                    self.logger.warning(f"⏳ Max connection attempts reached, waiting 30s...")
         | 
| 385 | 
            +
                                    await asyncio.sleep(30)
         | 
| 386 | 
            +
                                    connection_attempts = 0  # Reset after longer wait
         | 
| 387 | 
            +
                            
         | 
| 388 | 
            +
                            # Periodic health check
         | 
| 389 | 
            +
                            if self.connected and self.client:
         | 
| 390 | 
            +
                                try:
         | 
| 391 | 
            +
                                    await self.client.emit('ping')
         | 
| 392 | 
            +
                                except Exception as e:
         | 
| 393 | 
            +
                                    self.logger.warning(f"Health check failed: {e}")
         | 
| 394 | 
            +
                                    self.connected = False
         | 
| 395 | 
            +
                            
         | 
| 396 | 
            +
                            await asyncio.sleep(5)  # Check every 5 seconds
         | 
| 397 | 
            +
                            
         | 
| 398 | 
            +
                        except Exception as e:
         | 
| 399 | 
            +
                            self.logger.error(f"Error in connection manager loop: {e}")
         | 
| 400 | 
            +
                            await asyncio.sleep(5)
         | 
| 401 | 
            +
                
         | 
| 402 | 
            +
                # Compatibility methods for existing WebSocket server interface
         | 
| 403 | 
            +
                
         | 
| 404 | 
            +
                def broadcast_event(self, event_type: str, data: Dict[str, Any]):
         | 
| 405 | 
            +
                    """Legacy compatibility method for broadcasting events."""
         | 
| 406 | 
            +
                    if self.connected:
         | 
| 407 | 
            +
                        # Schedule emit in the connection thread
         | 
| 408 | 
            +
                        try:
         | 
| 409 | 
            +
                            loop = asyncio.new_event_loop()
         | 
| 410 | 
            +
                            loop.run_until_complete(self.emit_event(event_type, data))
         | 
| 411 | 
            +
                            loop.close()
         | 
| 412 | 
            +
                        except Exception as e:
         | 
| 413 | 
            +
                            self.logger.error(f"Error broadcasting event {event_type}: {e}")
         | 
| 414 | 
            +
                
         | 
| 415 | 
            +
                def session_started(self, session_id: str, launch_method: str, working_dir: str):
         | 
| 416 | 
            +
                    """Compatibility method for session start events."""
         | 
| 417 | 
            +
                    self.broadcast_event("session.start", {
         | 
| 418 | 
            +
                        "session_id": session_id,
         | 
| 419 | 
            +
                        "launch_method": launch_method,
         | 
| 420 | 
            +
                        "working_directory": working_dir
         | 
| 421 | 
            +
                    })
         | 
| 422 | 
            +
                
         | 
| 423 | 
            +
                def session_ended(self):
         | 
| 424 | 
            +
                    """Compatibility method for session end events."""
         | 
| 425 | 
            +
                    self.broadcast_event("session.end", {})
         | 
| 426 | 
            +
                
         | 
| 427 | 
            +
                def claude_status_changed(self, status: str, pid: Optional[int] = None, message: str = ""):
         | 
| 428 | 
            +
                    """Compatibility method for Claude status events."""
         | 
| 429 | 
            +
                    self.broadcast_event("claude.status", {
         | 
| 430 | 
            +
                        "status": status,
         | 
| 431 | 
            +
                        "pid": pid,
         | 
| 432 | 
            +
                        "message": message
         | 
| 433 | 
            +
                    })
         | 
| 434 | 
            +
                
         | 
| 435 | 
            +
                def agent_delegated(self, agent: str, task: str, status: str = "started"):
         | 
| 436 | 
            +
                    """Compatibility method for agent delegation events."""
         | 
| 437 | 
            +
                    self.broadcast_event("agent.delegation", {
         | 
| 438 | 
            +
                        "agent": agent,
         | 
| 439 | 
            +
                        "task": task,
         | 
| 440 | 
            +
                        "status": status
         | 
| 441 | 
            +
                    })
         | 
| 442 | 
            +
                
         | 
| 443 | 
            +
                def todo_updated(self, todos: List[Dict[str, Any]]):
         | 
| 444 | 
            +
                    """Compatibility method for todo update events."""
         | 
| 445 | 
            +
                    self.broadcast_event("todo.update", {
         | 
| 446 | 
            +
                        "todos": todos
         | 
| 447 | 
            +
                    })
         | 
| 448 | 
            +
                
         | 
| 449 | 
            +
                @property
         | 
| 450 | 
            +
                def running_status(self) -> bool:
         | 
| 451 | 
            +
                    """Compatibility property."""
         | 
| 452 | 
            +
                    return self.running
         | 
| 453 | 
            +
             | 
| 454 | 
            +
             | 
| 455 | 
            +
            # Global instance for easy access
         | 
| 456 | 
            +
            _client_manager: Optional[SocketIOClientManager] = None
         | 
| 457 | 
            +
             | 
| 458 | 
            +
             | 
| 459 | 
            +
            def get_client_manager() -> SocketIOClientManager:
         | 
| 460 | 
            +
                """Get or create the global client manager instance."""
         | 
| 461 | 
            +
                global _client_manager
         | 
| 462 | 
            +
                if _client_manager is None:
         | 
| 463 | 
            +
                    _client_manager = SocketIOClientManager()
         | 
| 464 | 
            +
                return _client_manager
         | 
| 465 | 
            +
             | 
| 466 | 
            +
             | 
| 467 | 
            +
            def start_client_manager():
         | 
| 468 | 
            +
                """Start the global client manager."""
         | 
| 469 | 
            +
                manager = get_client_manager()
         | 
| 470 | 
            +
                manager.start_connection_manager()
         | 
| 471 | 
            +
                return manager
         | 
| 472 | 
            +
             | 
| 473 | 
            +
             | 
| 474 | 
            +
            def stop_client_manager():
         | 
| 475 | 
            +
                """Stop the global client manager."""
         | 
| 476 | 
            +
                global _client_manager
         | 
| 477 | 
            +
                if _client_manager:
         | 
| 478 | 
            +
                    _client_manager.stop_connection_manager()
         | 
| 479 | 
            +
                    _client_manager = None
         | 
| @@ -0,0 +1,123 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            Core Service Interfaces and Base Classes
         | 
| 3 | 
            +
            ========================================
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            This module provides the core service interfaces and base classes for the
         | 
| 6 | 
            +
            Claude MPM framework. All services should inherit from these base classes
         | 
| 7 | 
            +
            and implement the appropriate interfaces.
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            Part of TSK-0046: Service Layer Architecture Reorganization
         | 
| 10 | 
            +
            """
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            from .interfaces import (
         | 
| 13 | 
            +
                # Core dependency injection
         | 
| 14 | 
            +
                IServiceContainer,
         | 
| 15 | 
            +
                ServiceType,
         | 
| 16 | 
            +
                
         | 
| 17 | 
            +
                # Configuration management
         | 
| 18 | 
            +
                IConfigurationService,
         | 
| 19 | 
            +
                IConfigurationManager,
         | 
| 20 | 
            +
                
         | 
| 21 | 
            +
                # Agent management
         | 
| 22 | 
            +
                IAgentRegistry,
         | 
| 23 | 
            +
                AgentMetadata,
         | 
| 24 | 
            +
                
         | 
| 25 | 
            +
                # Health monitoring
         | 
| 26 | 
            +
                IHealthMonitor,
         | 
| 27 | 
            +
                HealthStatus,
         | 
| 28 | 
            +
                
         | 
| 29 | 
            +
                # Caching
         | 
| 30 | 
            +
                IPromptCache,
         | 
| 31 | 
            +
                CacheEntry,
         | 
| 32 | 
            +
                
         | 
| 33 | 
            +
                # Template management
         | 
| 34 | 
            +
                ITemplateManager,
         | 
| 35 | 
            +
                TemplateRenderContext,
         | 
| 36 | 
            +
                
         | 
| 37 | 
            +
                # Factory patterns
         | 
| 38 | 
            +
                IServiceFactory,
         | 
| 39 | 
            +
                
         | 
| 40 | 
            +
                # Event system
         | 
| 41 | 
            +
                IEventBus,
         | 
| 42 | 
            +
                
         | 
| 43 | 
            +
                # Logging
         | 
| 44 | 
            +
                IStructuredLogger,
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
                # Service lifecycle
         | 
| 47 | 
            +
                IServiceLifecycle,
         | 
| 48 | 
            +
                
         | 
| 49 | 
            +
                # Error handling
         | 
| 50 | 
            +
                IErrorHandler,
         | 
| 51 | 
            +
                
         | 
| 52 | 
            +
                # Performance monitoring
         | 
| 53 | 
            +
                IPerformanceMonitor,
         | 
| 54 | 
            +
                
         | 
| 55 | 
            +
                # Cache service
         | 
| 56 | 
            +
                ICacheService,
         | 
| 57 | 
            +
                
         | 
| 58 | 
            +
                # Agent deployment
         | 
| 59 | 
            +
                AgentDeploymentInterface,
         | 
| 60 | 
            +
                
         | 
| 61 | 
            +
                # Memory service
         | 
| 62 | 
            +
                MemoryServiceInterface,
         | 
| 63 | 
            +
                
         | 
| 64 | 
            +
                # Hook service
         | 
| 65 | 
            +
                HookServiceInterface,
         | 
| 66 | 
            +
                
         | 
| 67 | 
            +
                # SocketIO service
         | 
| 68 | 
            +
                SocketIOServiceInterface,
         | 
| 69 | 
            +
                
         | 
| 70 | 
            +
                # Project analyzer
         | 
| 71 | 
            +
                ProjectAnalyzerInterface,
         | 
| 72 | 
            +
                
         | 
| 73 | 
            +
                # Ticket manager
         | 
| 74 | 
            +
                TicketManagerInterface,
         | 
| 75 | 
            +
                
         | 
| 76 | 
            +
                # Interface registry
         | 
| 77 | 
            +
                InterfaceRegistry,
         | 
| 78 | 
            +
            )
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            from .base import (
         | 
| 81 | 
            +
                BaseService,
         | 
| 82 | 
            +
                SyncBaseService,
         | 
| 83 | 
            +
                SingletonService,
         | 
| 84 | 
            +
            )
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            __all__ = [
         | 
| 87 | 
            +
                # Core interfaces
         | 
| 88 | 
            +
                'IServiceContainer',
         | 
| 89 | 
            +
                'ServiceType',
         | 
| 90 | 
            +
                'IConfigurationService',
         | 
| 91 | 
            +
                'IConfigurationManager',
         | 
| 92 | 
            +
                'IAgentRegistry',
         | 
| 93 | 
            +
                'AgentMetadata',
         | 
| 94 | 
            +
                'IHealthMonitor',
         | 
| 95 | 
            +
                'HealthStatus',
         | 
| 96 | 
            +
                'IPromptCache',
         | 
| 97 | 
            +
                'CacheEntry',
         | 
| 98 | 
            +
                'ITemplateManager',
         | 
| 99 | 
            +
                'TemplateRenderContext',
         | 
| 100 | 
            +
                'IServiceFactory',
         | 
| 101 | 
            +
                'IEventBus',
         | 
| 102 | 
            +
                'IStructuredLogger',
         | 
| 103 | 
            +
                'IServiceLifecycle',
         | 
| 104 | 
            +
                'IErrorHandler',
         | 
| 105 | 
            +
                'IPerformanceMonitor',
         | 
| 106 | 
            +
                'ICacheService',
         | 
| 107 | 
            +
                
         | 
| 108 | 
            +
                # Service interfaces
         | 
| 109 | 
            +
                'AgentDeploymentInterface',
         | 
| 110 | 
            +
                'MemoryServiceInterface',
         | 
| 111 | 
            +
                'HookServiceInterface',
         | 
| 112 | 
            +
                'SocketIOServiceInterface',
         | 
| 113 | 
            +
                'ProjectAnalyzerInterface',
         | 
| 114 | 
            +
                'TicketManagerInterface',
         | 
| 115 | 
            +
                
         | 
| 116 | 
            +
                # Registry
         | 
| 117 | 
            +
                'InterfaceRegistry',
         | 
| 118 | 
            +
                
         | 
| 119 | 
            +
                # Base classes
         | 
| 120 | 
            +
                'BaseService',
         | 
| 121 | 
            +
                'SyncBaseService',
         | 
| 122 | 
            +
                'SingletonService',
         | 
| 123 | 
            +
            ]
         |