claude-mpm 3.4.26__py3-none-any.whl → 3.5.0__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/INSTRUCTIONS.md +182 -299
- claude_mpm/agents/agent_loader.py +283 -57
- claude_mpm/agents/agent_loader_integration.py +6 -9
- claude_mpm/agents/base_agent.json +2 -1
- claude_mpm/agents/base_agent_loader.py +1 -1
- claude_mpm/cli/__init__.py +6 -10
- claude_mpm/cli/commands/__init__.py +0 -2
- claude_mpm/cli/commands/agents.py +1 -1
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/run.py +12 -0
- claude_mpm/cli/parser.py +0 -13
- claude_mpm/cli/utils.py +1 -1
- claude_mpm/config/__init__.py +44 -2
- claude_mpm/config/agent_config.py +348 -0
- claude_mpm/config/paths.py +322 -0
- claude_mpm/constants.py +0 -1
- claude_mpm/core/__init__.py +2 -5
- claude_mpm/core/agent_registry.py +63 -17
- claude_mpm/core/claude_runner.py +354 -43
- claude_mpm/core/config.py +7 -1
- claude_mpm/core/config_aliases.py +4 -3
- claude_mpm/core/config_paths.py +151 -0
- claude_mpm/core/factories.py +4 -50
- claude_mpm/core/logger.py +11 -13
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/init.py +37 -6
- claude_mpm/scripts/socketio_daemon.py +6 -2
- claude_mpm/services/__init__.py +71 -3
- claude_mpm/services/agents/__init__.py +85 -0
- claude_mpm/services/agents/deployment/__init__.py +21 -0
- claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
- claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
- claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
- claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
- claude_mpm/services/agents/memory/__init__.py +21 -0
- claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
- claude_mpm/services/agents/registry/__init__.py +29 -0
- claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
- claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
- claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
- claude_mpm/services/async_session_logger.py +584 -0
- claude_mpm/services/claude_session_logger.py +299 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
- claude_mpm/services/framework_claude_md_generator.py +4 -2
- claude_mpm/services/memory/__init__.py +17 -0
- claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
- claude_mpm/services/memory/cache/simple_cache.py +317 -0
- claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
- claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
- claude_mpm/services/optimized_hook_service.py +542 -0
- claude_mpm/services/project_registry.py +14 -8
- claude_mpm/services/response_tracker.py +237 -0
- claude_mpm/services/ticketing_service_original.py +4 -2
- claude_mpm/services/version_control/branch_strategy.py +3 -1
- claude_mpm/utils/paths.py +12 -10
- claude_mpm/utils/session_logging.py +114 -0
- claude_mpm/validation/agent_validator.py +2 -1
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
- /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
- /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.4.26.dist-info → claude_mpm-3.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Centralized configuration paths for claude-mpm.
|
|
3
|
+
|
|
4
|
+
This module provides a single source of truth for all configuration-related paths
|
|
5
|
+
throughout the codebase. All modules should import and use these constants
|
|
6
|
+
instead of hardcoding directory names.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigDirName(Enum):
|
|
15
|
+
"""Enum for configuration directory names to ensure type safety."""
|
|
16
|
+
CLAUDE_MPM = ".claude-mpm"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Core configuration directory name - MUST be consistent everywhere
|
|
20
|
+
CONFIG_DIR_NAME = ConfigDirName.CLAUDE_MPM.value
|
|
21
|
+
|
|
22
|
+
# Legacy name that should NOT be used (kept for reference during migration)
|
|
23
|
+
_LEGACY_CONFIG_DIR_NAME = ".claude-pm" # DO NOT USE THIS
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ConfigPaths:
|
|
27
|
+
"""Centralized configuration path constants."""
|
|
28
|
+
|
|
29
|
+
# Base directory names
|
|
30
|
+
CONFIG_DIR = ConfigDirName.CLAUDE_MPM.value
|
|
31
|
+
AGENTS_DIR = "agents"
|
|
32
|
+
BACKUPS_DIR = "backups"
|
|
33
|
+
TRACKING_DIR = "agent_tracking"
|
|
34
|
+
MEMORIES_DIR = "memories"
|
|
35
|
+
RESPONSES_DIR = "responses"
|
|
36
|
+
|
|
37
|
+
# Agent subdirectories
|
|
38
|
+
AGENT_PROJECT_SPECIFIC = "project-specific"
|
|
39
|
+
AGENT_USER_AGENTS = "user-agents"
|
|
40
|
+
AGENT_USER_DEFINED = "user-defined"
|
|
41
|
+
AGENT_SYSTEM = "templates"
|
|
42
|
+
|
|
43
|
+
# Configuration files
|
|
44
|
+
CONFIG_FILE = "config.json"
|
|
45
|
+
CONFIGURATION_YAML = "configuration.yaml"
|
|
46
|
+
LIFECYCLE_RECORDS = "lifecycle_records.json"
|
|
47
|
+
VERSION_FILE = "VERSION"
|
|
48
|
+
INSTRUCTIONS_FILE = "INSTRUCTIONS.md"
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def get_user_config_dir(cls) -> Path:
|
|
52
|
+
"""Get the user-level configuration directory."""
|
|
53
|
+
return Path.home() / cls.CONFIG_DIR
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def get_project_config_dir(cls, project_root: Optional[Path] = None) -> Path:
|
|
57
|
+
"""Get the project-level configuration directory."""
|
|
58
|
+
root = project_root or Path.cwd()
|
|
59
|
+
return root / cls.CONFIG_DIR
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def get_framework_config_dir(cls, framework_root: Path) -> Path:
|
|
63
|
+
"""Get the framework-level configuration directory."""
|
|
64
|
+
return framework_root / cls.CONFIG_DIR
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def get_user_agents_dir(cls) -> Path:
|
|
68
|
+
"""Get the user-level agents directory."""
|
|
69
|
+
return cls.get_user_config_dir() / cls.AGENTS_DIR
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def get_project_agents_dir(cls, project_root: Optional[Path] = None) -> Path:
|
|
73
|
+
"""Get the project-level agents directory."""
|
|
74
|
+
return cls.get_project_config_dir(project_root) / cls.AGENTS_DIR
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def get_tracking_dir(cls) -> Path:
|
|
78
|
+
"""Get the agent tracking directory."""
|
|
79
|
+
return cls.get_user_config_dir() / cls.TRACKING_DIR
|
|
80
|
+
|
|
81
|
+
@classmethod
|
|
82
|
+
def get_backups_dir(cls, base_dir: Optional[Path] = None) -> Path:
|
|
83
|
+
"""Get the backups directory."""
|
|
84
|
+
if base_dir:
|
|
85
|
+
return base_dir / cls.CONFIG_DIR / cls.BACKUPS_DIR
|
|
86
|
+
return cls.get_user_config_dir() / cls.BACKUPS_DIR
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def get_memories_dir(cls, base_dir: Optional[Path] = None) -> Path:
|
|
90
|
+
"""Get the memories directory."""
|
|
91
|
+
if base_dir:
|
|
92
|
+
return base_dir / cls.CONFIG_DIR / cls.MEMORIES_DIR
|
|
93
|
+
return cls.get_project_config_dir() / cls.MEMORIES_DIR
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def get_responses_dir(cls, base_dir: Optional[Path] = None) -> Path:
|
|
97
|
+
"""Get the responses directory."""
|
|
98
|
+
if base_dir:
|
|
99
|
+
return base_dir / cls.CONFIG_DIR / cls.RESPONSES_DIR
|
|
100
|
+
return cls.get_project_config_dir() / cls.RESPONSES_DIR
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def find_config_dir(cls, start_path: Optional[Path] = None) -> Optional[Path]:
|
|
104
|
+
"""
|
|
105
|
+
Find the nearest configuration directory by walking up the directory tree.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
start_path: Starting directory (defaults to current working directory)
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Path to the configuration directory if found, None otherwise
|
|
112
|
+
"""
|
|
113
|
+
current = start_path or Path.cwd()
|
|
114
|
+
|
|
115
|
+
# Check current directory and parents
|
|
116
|
+
for path in [current] + list(current.parents):
|
|
117
|
+
config_dir = path / cls.CONFIG_DIR
|
|
118
|
+
if config_dir.exists():
|
|
119
|
+
return config_dir
|
|
120
|
+
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
@classmethod
|
|
124
|
+
def validate_not_legacy(cls, path: Path) -> bool:
|
|
125
|
+
"""
|
|
126
|
+
Check if a path contains the legacy configuration directory name.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
path: Path to check
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
True if path is valid (not legacy), False if it contains legacy name
|
|
133
|
+
"""
|
|
134
|
+
path_str = str(path)
|
|
135
|
+
return _LEGACY_CONFIG_DIR_NAME not in path_str
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# Export commonly used paths as module-level constants for convenience
|
|
139
|
+
USER_CONFIG_DIR = ConfigPaths.get_user_config_dir()
|
|
140
|
+
USER_AGENTS_DIR = ConfigPaths.get_user_agents_dir()
|
|
141
|
+
USER_TRACKING_DIR = ConfigPaths.get_tracking_dir()
|
|
142
|
+
|
|
143
|
+
# Export all public symbols
|
|
144
|
+
__all__ = [
|
|
145
|
+
"ConfigDirName",
|
|
146
|
+
"ConfigPaths",
|
|
147
|
+
"CONFIG_DIR_NAME",
|
|
148
|
+
"USER_CONFIG_DIR",
|
|
149
|
+
"USER_AGENTS_DIR",
|
|
150
|
+
"USER_TRACKING_DIR",
|
|
151
|
+
]
|
claude_mpm/core/factories.py
CHANGED
|
@@ -12,9 +12,8 @@ from typing import Any, Dict, Optional, Type, TypeVar
|
|
|
12
12
|
from .container import DIContainer
|
|
13
13
|
from .logger import get_logger
|
|
14
14
|
from .config import Config
|
|
15
|
-
from ..services
|
|
16
|
-
|
|
17
|
-
from ..orchestration.base import BaseOrchestrator
|
|
15
|
+
from ..services import AgentDeploymentService
|
|
16
|
+
# Note: Orchestration functionality has been replaced by claude_runner
|
|
18
17
|
|
|
19
18
|
logger = get_logger(__name__)
|
|
20
19
|
|
|
@@ -32,52 +31,7 @@ class ServiceFactory(ABC):
|
|
|
32
31
|
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
"""Factory wrapper for creating orchestrator instances."""
|
|
37
|
-
|
|
38
|
-
def __init__(self):
|
|
39
|
-
"""Initialize the factory wrapper."""
|
|
40
|
-
self._factory = OrchestratorFactory()
|
|
41
|
-
|
|
42
|
-
def create(
|
|
43
|
-
self,
|
|
44
|
-
container: DIContainer,
|
|
45
|
-
orchestrator_type: Optional[str] = None,
|
|
46
|
-
**kwargs
|
|
47
|
-
) -> BaseOrchestrator:
|
|
48
|
-
"""
|
|
49
|
-
Create an orchestrator instance.
|
|
50
|
-
|
|
51
|
-
Args:
|
|
52
|
-
container: DI container
|
|
53
|
-
orchestrator_type: Type of orchestrator to create
|
|
54
|
-
**kwargs: Additional arguments for orchestrator
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
Orchestrator instance
|
|
58
|
-
"""
|
|
59
|
-
config = container.resolve(Config)
|
|
60
|
-
|
|
61
|
-
# Get orchestrator type from config if not provided
|
|
62
|
-
if orchestrator_type is None:
|
|
63
|
-
orchestrator_type = config.get('orchestrator.type', 'subprocess')
|
|
64
|
-
|
|
65
|
-
# Get orchestrator config
|
|
66
|
-
orch_config = config.get(f'orchestrator.{orchestrator_type}', {})
|
|
67
|
-
|
|
68
|
-
# Merge with provided kwargs
|
|
69
|
-
orch_config.update(kwargs)
|
|
70
|
-
|
|
71
|
-
# No hook manager injection needed - Claude Code hooks are external
|
|
72
|
-
|
|
73
|
-
# Create orchestrator
|
|
74
|
-
orchestrator = self._factory.create_orchestrator(
|
|
75
|
-
orchestrator_type,
|
|
76
|
-
**orch_config
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
logger.info(f"Created {orchestrator_type} orchestrator")
|
|
80
|
-
return orchestrator
|
|
34
|
+
# OrchestratorFactoryWrapper has been removed - orchestration replaced by claude_runner
|
|
81
35
|
|
|
82
36
|
|
|
83
37
|
class AgentServiceFactory(ServiceFactory):
|
|
@@ -204,7 +158,7 @@ class FactoryRegistry:
|
|
|
204
158
|
def __init__(self):
|
|
205
159
|
"""Initialize factory registry."""
|
|
206
160
|
self._factories: Dict[str, ServiceFactory] = {
|
|
207
|
-
'orchestrator'
|
|
161
|
+
# 'orchestrator' removed - replaced by claude_runner
|
|
208
162
|
'agent_service': AgentServiceFactory(),
|
|
209
163
|
'session_manager': SessionManagerFactory(),
|
|
210
164
|
'configuration': ConfigurationFactory(),
|
claude_mpm/core/logger.py
CHANGED
|
@@ -18,13 +18,8 @@ from enum import Enum
|
|
|
18
18
|
from collections import defaultdict
|
|
19
19
|
import functools
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
from rich.logging import RichHandler
|
|
24
|
-
from rich.console import Console
|
|
25
|
-
HAS_RICH = True
|
|
26
|
-
except ImportError:
|
|
27
|
-
HAS_RICH = False
|
|
21
|
+
# Rich support has been removed
|
|
22
|
+
HAS_RICH = False
|
|
28
23
|
|
|
29
24
|
|
|
30
25
|
class LogLevel(Enum):
|
|
@@ -150,7 +145,7 @@ def setup_logging(
|
|
|
150
145
|
log_file: Specific log file path (overrides log_dir)
|
|
151
146
|
console_output: Enable console output
|
|
152
147
|
file_output: Enable file output
|
|
153
|
-
use_rich:
|
|
148
|
+
use_rich: (Deprecated) Rich support has been removed
|
|
154
149
|
json_format: Use JSON format for structured logging
|
|
155
150
|
use_streaming: Use streaming handler for single-line INFO messages
|
|
156
151
|
|
|
@@ -187,10 +182,10 @@ def setup_logging(
|
|
|
187
182
|
# Use streaming handler for single-line INFO messages
|
|
188
183
|
console_handler = StreamingHandler(sys.stdout)
|
|
189
184
|
console_handler.setFormatter(simple_formatter)
|
|
190
|
-
elif use_rich and
|
|
191
|
-
|
|
192
|
-
console_handler =
|
|
193
|
-
console_handler.setFormatter(
|
|
185
|
+
elif use_rich and not json_format:
|
|
186
|
+
# Rich support has been removed, use standard handler
|
|
187
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
188
|
+
console_handler.setFormatter(simple_formatter)
|
|
194
189
|
else:
|
|
195
190
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
196
191
|
console_handler.setFormatter(formatter if json_format else simple_formatter)
|
|
@@ -212,7 +207,10 @@ def setup_logging(
|
|
|
212
207
|
else:
|
|
213
208
|
# Use default log directory
|
|
214
209
|
if log_dir is None:
|
|
215
|
-
|
|
210
|
+
# Use deployment root for logs to keep everything centralized
|
|
211
|
+
from claude_mpm.deployment_paths import get_project_root
|
|
212
|
+
deployment_root = get_project_root()
|
|
213
|
+
log_dir = deployment_root / ".claude-mpm" / "logs"
|
|
216
214
|
|
|
217
215
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
218
216
|
|
|
@@ -36,9 +36,9 @@ class ServiceRegistry:
|
|
|
36
36
|
|
|
37
37
|
def register_core_services(self) -> None:
|
|
38
38
|
"""Register all core framework services."""
|
|
39
|
-
from
|
|
39
|
+
from claude_mpm.services.memory.cache.shared_prompt_cache import SharedPromptCache
|
|
40
40
|
from ..services.ticket_manager import TicketManager
|
|
41
|
-
from ..services
|
|
41
|
+
from ..services import AgentDeploymentService
|
|
42
42
|
from .session_manager import SessionManager
|
|
43
43
|
from .agent_session_manager import AgentSessionManager
|
|
44
44
|
from .config import Config
|
|
@@ -77,6 +77,14 @@ class AgentInference {
|
|
|
77
77
|
// Direct event detection (highest confidence) - from design doc
|
|
78
78
|
if (eventType === 'SubagentStop' || subtype === 'subagent_stop') {
|
|
79
79
|
const agentName = this.extractAgentNameFromEvent(event);
|
|
80
|
+
// Log SubagentStop events for debugging
|
|
81
|
+
console.log('SubagentStop event detected:', {
|
|
82
|
+
agentName: agentName,
|
|
83
|
+
sessionId: sessionId,
|
|
84
|
+
eventType: eventType,
|
|
85
|
+
subtype: subtype,
|
|
86
|
+
rawAgentType: event.agent_type || data.agent_type
|
|
87
|
+
});
|
|
80
88
|
return {
|
|
81
89
|
type: 'subagent',
|
|
82
90
|
confidence: 'definitive',
|
|
@@ -98,6 +106,12 @@ class AgentInference {
|
|
|
98
106
|
if (toolName === 'Task') {
|
|
99
107
|
const agentName = this.extractSubagentTypeFromTask(event);
|
|
100
108
|
if (agentName) {
|
|
109
|
+
// Log Task delegations for debugging
|
|
110
|
+
console.log('Task delegation detected:', {
|
|
111
|
+
agentName: agentName,
|
|
112
|
+
sessionId: sessionId,
|
|
113
|
+
eventType: eventType
|
|
114
|
+
});
|
|
101
115
|
return {
|
|
102
116
|
type: 'subagent',
|
|
103
117
|
confidence: 'high',
|
|
@@ -141,7 +155,7 @@ class AgentInference {
|
|
|
141
155
|
return {
|
|
142
156
|
type: 'subagent',
|
|
143
157
|
confidence: 'high',
|
|
144
|
-
agentName: subagentType,
|
|
158
|
+
agentName: this.normalizeAgentName(subagentType),
|
|
145
159
|
reason: 'subagent_type field'
|
|
146
160
|
};
|
|
147
161
|
}
|
|
@@ -150,7 +164,7 @@ class AgentInference {
|
|
|
150
164
|
return {
|
|
151
165
|
type: 'subagent',
|
|
152
166
|
confidence: 'medium',
|
|
153
|
-
agentName: agentType,
|
|
167
|
+
agentName: this.normalizeAgentName(agentType),
|
|
154
168
|
reason: 'agent_type field'
|
|
155
169
|
};
|
|
156
170
|
}
|
|
@@ -160,7 +174,7 @@ class AgentInference {
|
|
|
160
174
|
return {
|
|
161
175
|
type: 'subagent',
|
|
162
176
|
confidence: 'high',
|
|
163
|
-
agentName: data.delegation_details.agent_type,
|
|
177
|
+
agentName: this.normalizeAgentName(data.delegation_details.agent_type),
|
|
164
178
|
reason: 'delegation_details'
|
|
165
179
|
};
|
|
166
180
|
}
|
|
@@ -169,12 +183,30 @@ class AgentInference {
|
|
|
169
183
|
if (event.type && event.type.startsWith('hook.')) {
|
|
170
184
|
// Extract the hook type
|
|
171
185
|
const hookType = event.type.replace('hook.', '');
|
|
186
|
+
|
|
187
|
+
// Handle SubagentStart events
|
|
188
|
+
if (hookType === 'subagent_start' || (data.hook_event_name === 'SubagentStart')) {
|
|
189
|
+
const rawAgentName = data.agent_type || data.agent_id || 'Subagent';
|
|
190
|
+
console.log('SubagentStart event from Socket.IO:', {
|
|
191
|
+
agentName: rawAgentName,
|
|
192
|
+
sessionId: sessionId,
|
|
193
|
+
hookType: hookType
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
type: 'subagent',
|
|
197
|
+
confidence: 'definitive',
|
|
198
|
+
agentName: this.normalizeAgentName(rawAgentName),
|
|
199
|
+
reason: 'Socket.IO hook SubagentStart'
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Handle SubagentStop events
|
|
172
204
|
if (hookType === 'subagent_stop' || (data.hook_event_name === 'SubagentStop')) {
|
|
173
|
-
const
|
|
205
|
+
const rawAgentName = data.agent_type || data.agent_id || 'Subagent';
|
|
174
206
|
return {
|
|
175
207
|
type: 'subagent',
|
|
176
208
|
confidence: 'high',
|
|
177
|
-
agentName:
|
|
209
|
+
agentName: this.normalizeAgentName(rawAgentName),
|
|
178
210
|
reason: 'Socket.IO hook SubagentStop'
|
|
179
211
|
};
|
|
180
212
|
}
|
|
@@ -189,33 +221,77 @@ class AgentInference {
|
|
|
189
221
|
};
|
|
190
222
|
}
|
|
191
223
|
|
|
224
|
+
/**
|
|
225
|
+
* Normalize agent name from lowercase/underscore format to display format
|
|
226
|
+
* @param {string} agentName - Raw agent name (e.g., 'engineer', 'test_integration')
|
|
227
|
+
* @returns {string} - Normalized display name (e.g., 'Engineer Agent', 'Test Integration Agent')
|
|
228
|
+
*/
|
|
229
|
+
normalizeAgentName(agentName) {
|
|
230
|
+
if (!agentName) return 'Unknown';
|
|
231
|
+
|
|
232
|
+
// Agent name mapping from raw format to display format
|
|
233
|
+
const agentNameMap = {
|
|
234
|
+
'engineer': 'Engineer Agent',
|
|
235
|
+
'research': 'Research Agent',
|
|
236
|
+
'qa': 'QA Agent',
|
|
237
|
+
'documentation': 'Documentation Agent',
|
|
238
|
+
'security': 'Security Agent',
|
|
239
|
+
'ops': 'Ops Agent',
|
|
240
|
+
'version_control': 'Version Control Agent',
|
|
241
|
+
'data_engineer': 'Data Engineer Agent',
|
|
242
|
+
'test_integration': 'Test Integration Agent',
|
|
243
|
+
'pm': 'PM Agent'
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Check if we have a direct mapping
|
|
247
|
+
const normalized = agentNameMap[agentName.toLowerCase()];
|
|
248
|
+
if (normalized) {
|
|
249
|
+
return normalized;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// If no direct mapping, apply basic formatting:
|
|
253
|
+
// Convert underscore to space, capitalize words, and add "Agent" if not present
|
|
254
|
+
let formatted = agentName
|
|
255
|
+
.replace(/_/g, ' ')
|
|
256
|
+
.split(' ')
|
|
257
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
258
|
+
.join(' ');
|
|
259
|
+
|
|
260
|
+
// Add "Agent" suffix if not already present
|
|
261
|
+
if (!formatted.toLowerCase().includes('agent')) {
|
|
262
|
+
formatted += ' Agent';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return formatted;
|
|
266
|
+
}
|
|
267
|
+
|
|
192
268
|
/**
|
|
193
269
|
* Extract subagent type from Task tool parameters
|
|
194
270
|
* @param {Object} event - Event with Task tool
|
|
195
271
|
* @returns {string|null} - Subagent type or null
|
|
196
272
|
*/
|
|
197
273
|
extractSubagentTypeFromTask(event) {
|
|
274
|
+
let rawAgentName = null;
|
|
275
|
+
|
|
198
276
|
// Check tool_parameters directly
|
|
199
277
|
if (event.tool_parameters?.subagent_type) {
|
|
200
|
-
|
|
278
|
+
rawAgentName = event.tool_parameters.subagent_type;
|
|
201
279
|
}
|
|
202
|
-
|
|
203
280
|
// Check nested in data.tool_parameters (hook events)
|
|
204
|
-
if (event.data?.tool_parameters?.subagent_type) {
|
|
205
|
-
|
|
281
|
+
else if (event.data?.tool_parameters?.subagent_type) {
|
|
282
|
+
rawAgentName = event.data.tool_parameters.subagent_type;
|
|
206
283
|
}
|
|
207
|
-
|
|
208
284
|
// Check delegation_details (new structure)
|
|
209
|
-
if (event.data?.delegation_details?.agent_type) {
|
|
210
|
-
|
|
285
|
+
else if (event.data?.delegation_details?.agent_type) {
|
|
286
|
+
rawAgentName = event.data.delegation_details.agent_type;
|
|
211
287
|
}
|
|
212
|
-
|
|
213
288
|
// Check tool_input fallback
|
|
214
|
-
if (event.tool_input?.subagent_type) {
|
|
215
|
-
|
|
289
|
+
else if (event.tool_input?.subagent_type) {
|
|
290
|
+
rawAgentName = event.tool_input.subagent_type;
|
|
216
291
|
}
|
|
217
292
|
|
|
218
|
-
|
|
293
|
+
// Normalize the agent name before returning
|
|
294
|
+
return rawAgentName ? this.normalizeAgentName(rawAgentName) : null;
|
|
219
295
|
}
|
|
220
296
|
|
|
221
297
|
/**
|
|
@@ -235,40 +311,40 @@ class AgentInference {
|
|
|
235
311
|
|
|
236
312
|
// 2. Direct subagent_type field
|
|
237
313
|
if (event.subagent_type && event.subagent_type !== 'unknown') {
|
|
238
|
-
return event.subagent_type;
|
|
314
|
+
return this.normalizeAgentName(event.subagent_type);
|
|
239
315
|
}
|
|
240
316
|
if (data.subagent_type && data.subagent_type !== 'unknown') {
|
|
241
|
-
return data.subagent_type;
|
|
317
|
+
return this.normalizeAgentName(data.subagent_type);
|
|
242
318
|
}
|
|
243
319
|
|
|
244
320
|
// 2.5. Check delegation_details
|
|
245
321
|
if (data.delegation_details?.agent_type && data.delegation_details.agent_type !== 'unknown') {
|
|
246
|
-
return data.delegation_details.agent_type;
|
|
322
|
+
return this.normalizeAgentName(data.delegation_details.agent_type);
|
|
247
323
|
}
|
|
248
324
|
|
|
249
325
|
// 3. Agent type fields (but not 'main' or 'unknown')
|
|
250
326
|
if (event.agent_type && !['main', 'unknown'].includes(event.agent_type)) {
|
|
251
|
-
return event.agent_type;
|
|
327
|
+
return this.normalizeAgentName(event.agent_type);
|
|
252
328
|
}
|
|
253
329
|
if (data.agent_type && !['main', 'unknown'].includes(data.agent_type)) {
|
|
254
|
-
return data.agent_type;
|
|
330
|
+
return this.normalizeAgentName(data.agent_type);
|
|
255
331
|
}
|
|
256
332
|
|
|
257
333
|
// 4. Agent ID field as fallback
|
|
258
334
|
if (event.agent_id && !['main', 'unknown'].includes(event.agent_id)) {
|
|
259
|
-
return event.agent_id;
|
|
335
|
+
return this.normalizeAgentName(event.agent_id);
|
|
260
336
|
}
|
|
261
337
|
if (data.agent_id && !['main', 'unknown'].includes(data.agent_id)) {
|
|
262
|
-
return data.agent_id;
|
|
338
|
+
return this.normalizeAgentName(data.agent_id);
|
|
263
339
|
}
|
|
264
340
|
|
|
265
341
|
// 5. Other fallbacks
|
|
266
342
|
if (event.agent && event.agent !== 'unknown') {
|
|
267
|
-
return event.agent;
|
|
343
|
+
return this.normalizeAgentName(event.agent);
|
|
268
344
|
}
|
|
269
345
|
|
|
270
346
|
if (event.name && event.name !== 'unknown') {
|
|
271
|
-
return event.name;
|
|
347
|
+
return this.normalizeAgentName(event.name);
|
|
272
348
|
}
|
|
273
349
|
|
|
274
350
|
// Default fallback
|
|
@@ -324,9 +324,10 @@ class EventProcessor {
|
|
|
324
324
|
|
|
325
325
|
return filteredInstances.map((instance, index) => {
|
|
326
326
|
const agentName = instance.agentName;
|
|
327
|
-
const timestamp = this.formatTimestamp(instance.timestamp);
|
|
327
|
+
const timestamp = this.formatTimestamp(instance.firstTimestamp || instance.timestamp);
|
|
328
328
|
const delegationType = instance.isImplied ? 'implied' : 'explicit';
|
|
329
|
-
|
|
329
|
+
// Fix: Use totalEventCount which is the actual property name from getUniqueAgentInstances()
|
|
330
|
+
const eventCount = instance.totalEventCount || instance.eventCount || 0;
|
|
330
331
|
|
|
331
332
|
const onclickString = `dashboard.selectCard('agents', ${index}, 'agent_instance', '${instance.id}'); dashboard.showAgentInstanceDetails('${instance.id}');`;
|
|
332
333
|
|