claude-mpm 4.1.0__py3-none-any.whl → 4.1.2__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/BUILD_NUMBER +1 -1
- claude_mpm/VERSION +1 -1
- claude_mpm/__main__.py +1 -1
- claude_mpm/agents/BASE_PM.md +74 -46
- claude_mpm/agents/INSTRUCTIONS.md +11 -153
- claude_mpm/agents/WORKFLOW.md +61 -321
- claude_mpm/agents/__init__.py +11 -11
- claude_mpm/agents/agent_loader.py +23 -20
- claude_mpm/agents/agent_loader_integration.py +1 -1
- claude_mpm/agents/agents_metadata.py +27 -0
- claude_mpm/agents/async_agent_loader.py +5 -8
- claude_mpm/agents/base_agent_loader.py +36 -25
- claude_mpm/agents/frontmatter_validator.py +6 -6
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +9 -9
- claude_mpm/agents/templates/api_qa.json +47 -2
- claude_mpm/agents/templates/imagemagick.json +256 -0
- claude_mpm/agents/templates/qa.json +41 -2
- claude_mpm/agents/templates/ticketing.json +5 -5
- claude_mpm/agents/templates/web_qa.json +133 -58
- claude_mpm/agents/templates/web_ui.json +3 -3
- claude_mpm/cli/__init__.py +51 -46
- claude_mpm/cli/__main__.py +1 -1
- claude_mpm/cli/commands/__init__.py +10 -12
- claude_mpm/cli/commands/agent_manager.py +186 -181
- claude_mpm/cli/commands/agents.py +271 -268
- claude_mpm/cli/commands/aggregate.py +30 -29
- claude_mpm/cli/commands/cleanup.py +50 -44
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +25 -25
- claude_mpm/cli/commands/config.py +162 -127
- claude_mpm/cli/commands/doctor.py +52 -62
- claude_mpm/cli/commands/info.py +37 -25
- claude_mpm/cli/commands/mcp.py +3 -7
- claude_mpm/cli/commands/mcp_command_router.py +14 -18
- claude_mpm/cli/commands/mcp_install_commands.py +28 -23
- claude_mpm/cli/commands/mcp_pipx_config.py +58 -49
- claude_mpm/cli/commands/mcp_server_commands.py +23 -17
- claude_mpm/cli/commands/memory.py +192 -141
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +120 -84
- claude_mpm/cli/commands/run_config_checker.py +4 -5
- claude_mpm/cli/commands/socketio_monitor.py +17 -19
- claude_mpm/cli/commands/tickets.py +92 -92
- claude_mpm/cli/parser.py +1 -5
- claude_mpm/cli/parsers/__init__.py +1 -1
- claude_mpm/cli/parsers/agent_manager_parser.py +50 -98
- claude_mpm/cli/parsers/agents_parser.py +2 -3
- claude_mpm/cli/parsers/base_parser.py +7 -5
- claude_mpm/cli/parsers/mcp_parser.py +4 -2
- claude_mpm/cli/parsers/monitor_parser.py +26 -18
- claude_mpm/cli/shared/__init__.py +10 -10
- claude_mpm/cli/shared/argument_patterns.py +57 -71
- claude_mpm/cli/shared/base_command.py +61 -53
- claude_mpm/cli/shared/error_handling.py +62 -58
- claude_mpm/cli/shared/output_formatters.py +78 -77
- claude_mpm/cli/startup_logging.py +204 -172
- claude_mpm/cli/utils.py +10 -11
- claude_mpm/cli_module/__init__.py +1 -1
- claude_mpm/cli_module/args.py +1 -1
- claude_mpm/cli_module/migration_example.py +5 -5
- claude_mpm/config/__init__.py +9 -9
- claude_mpm/config/agent_config.py +15 -14
- claude_mpm/config/experimental_features.py +4 -4
- claude_mpm/config/paths.py +0 -1
- claude_mpm/config/socketio_config.py +5 -6
- claude_mpm/constants.py +1 -2
- claude_mpm/core/__init__.py +8 -8
- claude_mpm/core/agent_name_normalizer.py +1 -1
- claude_mpm/core/agent_registry.py +20 -23
- claude_mpm/core/agent_session_manager.py +3 -3
- claude_mpm/core/base_service.py +7 -15
- claude_mpm/core/cache.py +4 -6
- claude_mpm/core/claude_runner.py +85 -113
- claude_mpm/core/config.py +43 -28
- claude_mpm/core/config_aliases.py +0 -9
- claude_mpm/core/config_constants.py +52 -30
- claude_mpm/core/constants.py +0 -1
- claude_mpm/core/container.py +18 -27
- claude_mpm/core/exceptions.py +2 -2
- claude_mpm/core/factories.py +10 -12
- claude_mpm/core/framework_loader.py +581 -280
- claude_mpm/core/hook_manager.py +26 -22
- claude_mpm/core/hook_performance_config.py +58 -47
- claude_mpm/core/injectable_service.py +1 -1
- claude_mpm/core/interactive_session.py +61 -152
- claude_mpm/core/interfaces.py +1 -100
- claude_mpm/core/lazy.py +5 -5
- claude_mpm/core/log_manager.py +587 -0
- claude_mpm/core/logger.py +125 -8
- claude_mpm/core/logging_config.py +15 -15
- claude_mpm/core/minimal_framework_loader.py +5 -8
- claude_mpm/core/oneshot_session.py +15 -33
- claude_mpm/core/optimized_agent_loader.py +4 -6
- claude_mpm/core/optimized_startup.py +2 -1
- claude_mpm/core/output_style_manager.py +147 -106
- claude_mpm/core/pm_hook_interceptor.py +0 -1
- claude_mpm/core/service_registry.py +11 -8
- claude_mpm/core/session_manager.py +1 -2
- claude_mpm/core/shared/__init__.py +1 -1
- claude_mpm/core/shared/config_loader.py +101 -97
- claude_mpm/core/shared/path_resolver.py +72 -68
- claude_mpm/core/shared/singleton_manager.py +56 -50
- claude_mpm/core/socketio_pool.py +26 -6
- claude_mpm/core/tool_access_control.py +4 -5
- claude_mpm/core/typing_utils.py +50 -59
- claude_mpm/core/unified_agent_registry.py +14 -19
- claude_mpm/core/unified_config.py +4 -6
- claude_mpm/core/unified_paths.py +197 -109
- claude_mpm/dashboard/open_dashboard.py +2 -4
- claude_mpm/experimental/cli_enhancements.py +51 -36
- claude_mpm/generators/agent_profile_generator.py +2 -4
- claude_mpm/hooks/base_hook.py +1 -2
- claude_mpm/hooks/claude_hooks/connection_pool.py +72 -26
- claude_mpm/hooks/claude_hooks/event_handlers.py +93 -38
- claude_mpm/hooks/claude_hooks/hook_handler.py +130 -76
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/memory_integration.py +2 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +15 -11
- claude_mpm/hooks/claude_hooks/tool_analysis.py +12 -18
- claude_mpm/hooks/memory_integration_hook.py +5 -5
- claude_mpm/hooks/tool_call_interceptor.py +1 -1
- claude_mpm/hooks/validation_hooks.py +4 -4
- claude_mpm/init.py +4 -9
- claude_mpm/models/__init__.py +2 -2
- claude_mpm/models/agent_session.py +11 -14
- claude_mpm/scripts/mcp_server.py +20 -11
- claude_mpm/scripts/mcp_wrapper.py +5 -5
- claude_mpm/scripts/mpm_doctor.py +321 -0
- claude_mpm/scripts/socketio_daemon.py +28 -25
- claude_mpm/scripts/socketio_daemon_hardened.py +298 -258
- claude_mpm/scripts/socketio_server_manager.py +116 -95
- claude_mpm/services/__init__.py +49 -49
- claude_mpm/services/agent_capabilities_service.py +12 -18
- claude_mpm/services/agents/__init__.py +22 -22
- claude_mpm/services/agents/agent_builder.py +140 -119
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +9 -9
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +19 -20
- claude_mpm/services/agents/deployment/agent_definition_factory.py +1 -5
- claude_mpm/services/agents/deployment/agent_deployment.py +136 -106
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -8
- claude_mpm/services/agents/deployment/agent_environment_manager.py +2 -7
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +6 -10
- claude_mpm/services/agents/deployment/agent_format_converter.py +11 -15
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +2 -3
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +5 -5
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +13 -19
- claude_mpm/services/agents/deployment/agent_restore_handler.py +0 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +26 -35
- claude_mpm/services/agents/deployment/agent_validator.py +0 -1
- claude_mpm/services/agents/deployment/agent_version_manager.py +7 -9
- claude_mpm/services/agents/deployment/agent_versioning.py +3 -3
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +6 -7
- claude_mpm/services/agents/deployment/async_agent_deployment.py +51 -38
- claude_mpm/services/agents/deployment/config/__init__.py +1 -1
- claude_mpm/services/agents/deployment/config/deployment_config.py +7 -8
- claude_mpm/services/agents/deployment/deployment_type_detector.py +1 -1
- claude_mpm/services/agents/deployment/deployment_wrapper.py +18 -18
- claude_mpm/services/agents/deployment/facade/__init__.py +1 -1
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +0 -3
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +3 -4
- claude_mpm/services/agents/deployment/interface_adapter.py +5 -7
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +345 -276
- claude_mpm/services/agents/deployment/pipeline/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +6 -4
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +3 -3
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +2 -2
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +14 -13
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +0 -1
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +1 -1
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +8 -9
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +1 -1
- claude_mpm/services/agents/deployment/processors/__init__.py +1 -1
- claude_mpm/services/agents/deployment/processors/agent_processor.py +20 -16
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +5 -12
- claude_mpm/services/agents/deployment/results/__init__.py +1 -1
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +1 -1
- claude_mpm/services/agents/deployment/strategies/__init__.py +2 -2
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +1 -7
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +1 -4
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +2 -3
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +3 -7
- claude_mpm/services/agents/deployment/validation/__init__.py +1 -1
- claude_mpm/services/agents/deployment/validation/agent_validator.py +1 -1
- claude_mpm/services/agents/deployment/validation/template_validator.py +2 -2
- claude_mpm/services/agents/deployment/validation/validation_result.py +2 -6
- claude_mpm/services/agents/loading/__init__.py +1 -1
- claude_mpm/services/agents/loading/agent_profile_loader.py +6 -12
- claude_mpm/services/agents/loading/base_agent_manager.py +5 -5
- claude_mpm/services/agents/loading/framework_agent_loader.py +2 -4
- claude_mpm/services/agents/management/__init__.py +1 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +1 -3
- claude_mpm/services/agents/management/agent_management_service.py +5 -9
- claude_mpm/services/agents/memory/__init__.py +4 -4
- claude_mpm/services/agents/memory/agent_memory_manager.py +280 -160
- claude_mpm/services/agents/memory/agent_persistence_service.py +0 -2
- claude_mpm/services/agents/memory/content_manager.py +44 -38
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +10 -6
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +30 -27
- claude_mpm/services/agents/registry/modification_tracker.py +3 -6
- claude_mpm/services/async_session_logger.py +1 -2
- claude_mpm/services/claude_session_logger.py +1 -2
- claude_mpm/services/command_deployment_service.py +173 -0
- claude_mpm/services/command_handler_service.py +20 -22
- claude_mpm/services/core/__init__.py +25 -25
- claude_mpm/services/core/base.py +0 -5
- claude_mpm/services/core/interfaces/__init__.py +32 -32
- claude_mpm/services/core/interfaces/agent.py +0 -21
- claude_mpm/services/core/interfaces/communication.py +0 -27
- claude_mpm/services/core/interfaces/infrastructure.py +0 -56
- claude_mpm/services/core/interfaces/service.py +0 -29
- claude_mpm/services/diagnostics/__init__.py +1 -1
- claude_mpm/services/diagnostics/checks/__init__.py +6 -6
- claude_mpm/services/diagnostics/checks/agent_check.py +89 -80
- claude_mpm/services/diagnostics/checks/base_check.py +12 -16
- claude_mpm/services/diagnostics/checks/claude_desktop_check.py +84 -81
- claude_mpm/services/diagnostics/checks/common_issues_check.py +99 -91
- claude_mpm/services/diagnostics/checks/configuration_check.py +82 -77
- claude_mpm/services/diagnostics/checks/filesystem_check.py +67 -68
- claude_mpm/services/diagnostics/checks/installation_check.py +254 -94
- claude_mpm/services/diagnostics/checks/mcp_check.py +90 -88
- claude_mpm/services/diagnostics/checks/monitor_check.py +75 -76
- claude_mpm/services/diagnostics/checks/startup_log_check.py +67 -73
- claude_mpm/services/diagnostics/diagnostic_runner.py +67 -59
- claude_mpm/services/diagnostics/doctor_reporter.py +107 -70
- claude_mpm/services/diagnostics/models.py +21 -19
- claude_mpm/services/event_aggregator.py +10 -17
- claude_mpm/services/event_bus/__init__.py +1 -1
- claude_mpm/services/event_bus/config.py +54 -35
- claude_mpm/services/event_bus/event_bus.py +76 -71
- claude_mpm/services/event_bus/relay.py +74 -64
- claude_mpm/services/events/__init__.py +11 -11
- claude_mpm/services/events/consumers/__init__.py +3 -3
- claude_mpm/services/events/consumers/dead_letter.py +71 -63
- claude_mpm/services/events/consumers/logging.py +39 -37
- claude_mpm/services/events/consumers/metrics.py +56 -57
- claude_mpm/services/events/consumers/socketio.py +82 -81
- claude_mpm/services/events/core.py +110 -99
- claude_mpm/services/events/interfaces.py +56 -72
- claude_mpm/services/events/producers/__init__.py +1 -1
- claude_mpm/services/events/producers/hook.py +38 -38
- claude_mpm/services/events/producers/system.py +46 -44
- claude_mpm/services/exceptions.py +81 -80
- claude_mpm/services/framework_claude_md_generator/__init__.py +2 -4
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +3 -5
- claude_mpm/services/framework_claude_md_generator/content_validator.py +1 -1
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +4 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +0 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +0 -2
- claude_mpm/services/framework_claude_md_generator/version_manager.py +4 -5
- claude_mpm/services/hook_service.py +6 -9
- claude_mpm/services/infrastructure/__init__.py +1 -1
- claude_mpm/services/infrastructure/context_preservation.py +8 -12
- claude_mpm/services/infrastructure/monitoring.py +21 -23
- claude_mpm/services/mcp_gateway/__init__.py +37 -37
- claude_mpm/services/mcp_gateway/auto_configure.py +95 -103
- claude_mpm/services/mcp_gateway/config/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/config/config_loader.py +23 -25
- claude_mpm/services/mcp_gateway/config/config_schema.py +5 -5
- claude_mpm/services/mcp_gateway/config/configuration.py +9 -6
- claude_mpm/services/mcp_gateway/core/__init__.py +10 -10
- claude_mpm/services/mcp_gateway/core/base.py +0 -3
- claude_mpm/services/mcp_gateway/core/interfaces.py +1 -38
- claude_mpm/services/mcp_gateway/core/process_pool.py +99 -93
- claude_mpm/services/mcp_gateway/core/singleton_manager.py +65 -62
- claude_mpm/services/mcp_gateway/core/startup_verification.py +75 -74
- claude_mpm/services/mcp_gateway/main.py +2 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +5 -8
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +1 -1
- claude_mpm/services/mcp_gateway/server/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +12 -19
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +4 -3
- claude_mpm/services/mcp_gateway/server/stdio_server.py +79 -71
- claude_mpm/services/mcp_gateway/tools/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +5 -6
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +13 -22
- claude_mpm/services/mcp_gateway/tools/health_check_tool.py +79 -78
- claude_mpm/services/mcp_gateway/tools/hello_world.py +12 -14
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +42 -49
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +51 -55
- claude_mpm/services/memory/__init__.py +3 -3
- claude_mpm/services/memory/builder.py +3 -6
- claude_mpm/services/memory/cache/__init__.py +1 -1
- claude_mpm/services/memory/cache/shared_prompt_cache.py +3 -5
- claude_mpm/services/memory/cache/simple_cache.py +1 -1
- claude_mpm/services/memory/indexed_memory.py +5 -7
- claude_mpm/services/memory/optimizer.py +7 -10
- claude_mpm/services/memory/router.py +8 -9
- claude_mpm/services/memory_hook_service.py +48 -34
- claude_mpm/services/monitor_build_service.py +77 -73
- claude_mpm/services/port_manager.py +130 -108
- claude_mpm/services/project/analyzer.py +12 -10
- claude_mpm/services/project/registry.py +11 -11
- claude_mpm/services/recovery_manager.py +10 -19
- claude_mpm/services/response_tracker.py +0 -1
- claude_mpm/services/runner_configuration_service.py +19 -20
- claude_mpm/services/session_management_service.py +7 -11
- claude_mpm/services/shared/__init__.py +1 -1
- claude_mpm/services/shared/async_service_base.py +58 -50
- claude_mpm/services/shared/config_service_base.py +73 -67
- claude_mpm/services/shared/lifecycle_service_base.py +82 -78
- claude_mpm/services/shared/manager_base.py +94 -82
- claude_mpm/services/shared/service_factory.py +96 -98
- claude_mpm/services/socketio/__init__.py +3 -3
- claude_mpm/services/socketio/client_proxy.py +5 -5
- claude_mpm/services/socketio/event_normalizer.py +199 -181
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +5 -4
- claude_mpm/services/socketio/handlers/connection.py +163 -136
- claude_mpm/services/socketio/handlers/file.py +13 -14
- claude_mpm/services/socketio/handlers/git.py +12 -7
- claude_mpm/services/socketio/handlers/hook.py +49 -44
- claude_mpm/services/socketio/handlers/memory.py +0 -1
- claude_mpm/services/socketio/handlers/project.py +0 -1
- claude_mpm/services/socketio/handlers/registry.py +37 -19
- claude_mpm/services/socketio/migration_utils.py +98 -84
- claude_mpm/services/socketio/server/__init__.py +1 -1
- claude_mpm/services/socketio/server/broadcaster.py +81 -87
- claude_mpm/services/socketio/server/core.py +65 -54
- claude_mpm/services/socketio/server/eventbus_integration.py +95 -56
- claude_mpm/services/socketio/server/main.py +64 -38
- claude_mpm/services/socketio_client_manager.py +10 -12
- claude_mpm/services/subprocess_launcher_service.py +4 -7
- claude_mpm/services/system_instructions_service.py +13 -14
- claude_mpm/services/ticket_manager.py +2 -2
- claude_mpm/services/utility_service.py +5 -13
- claude_mpm/services/version_control/__init__.py +16 -16
- claude_mpm/services/version_control/branch_strategy.py +5 -8
- claude_mpm/services/version_control/conflict_resolution.py +9 -23
- claude_mpm/services/version_control/git_operations.py +5 -7
- claude_mpm/services/version_control/semantic_versioning.py +16 -17
- claude_mpm/services/version_control/version_parser.py +13 -18
- claude_mpm/services/version_service.py +10 -11
- claude_mpm/storage/__init__.py +1 -1
- claude_mpm/storage/state_storage.py +22 -28
- claude_mpm/utils/__init__.py +6 -6
- claude_mpm/utils/agent_dependency_loader.py +47 -33
- claude_mpm/utils/config_manager.py +11 -14
- claude_mpm/utils/dependency_cache.py +1 -1
- claude_mpm/utils/dependency_manager.py +13 -17
- claude_mpm/utils/dependency_strategies.py +8 -10
- claude_mpm/utils/environment_context.py +3 -9
- claude_mpm/utils/error_handler.py +3 -13
- claude_mpm/utils/file_utils.py +1 -1
- claude_mpm/utils/path_operations.py +8 -12
- claude_mpm/utils/robust_installer.py +110 -33
- claude_mpm/utils/subprocess_utils.py +5 -6
- claude_mpm/validation/agent_validator.py +3 -6
- claude_mpm/validation/frontmatter_validator.py +1 -1
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/METADATA +1 -1
- claude_mpm-4.1.2.dist-info/RECORD +498 -0
- claude_mpm-4.1.0.dist-info/RECORD +0 -494
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.0.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
|
@@ -11,7 +11,7 @@ import re
|
|
|
11
11
|
import subprocess
|
|
12
12
|
import sys
|
|
13
13
|
from datetime import datetime
|
|
14
|
-
from typing import
|
|
14
|
+
from typing import Optional
|
|
15
15
|
|
|
16
16
|
# Import tool analysis with fallback for direct execution
|
|
17
17
|
try:
|
|
@@ -221,7 +221,7 @@ class EventHandlers:
|
|
|
221
221
|
self.hook_handler._track_delegation(session_id, agent_type, request_data)
|
|
222
222
|
|
|
223
223
|
if DEBUG:
|
|
224
|
-
print(
|
|
224
|
+
print(" - Delegation tracked successfully", file=sys.stderr)
|
|
225
225
|
print(
|
|
226
226
|
f" - Request data keys: {list(request_data.keys())}",
|
|
227
227
|
file=sys.stderr,
|
|
@@ -257,7 +257,56 @@ class EventHandlers:
|
|
|
257
257
|
"", "subagent_start", subagent_start_data
|
|
258
258
|
)
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
# Log agent prompt if LogManager is available
|
|
261
|
+
try:
|
|
262
|
+
from claude_mpm.core.log_manager import get_log_manager
|
|
263
|
+
|
|
264
|
+
log_manager = get_log_manager()
|
|
265
|
+
|
|
266
|
+
# Prepare prompt content
|
|
267
|
+
prompt_content = tool_input.get("prompt", "")
|
|
268
|
+
if not prompt_content:
|
|
269
|
+
prompt_content = tool_input.get("description", "")
|
|
270
|
+
|
|
271
|
+
if prompt_content:
|
|
272
|
+
import asyncio
|
|
273
|
+
|
|
274
|
+
# Prepare metadata
|
|
275
|
+
metadata = {
|
|
276
|
+
"agent_type": agent_type,
|
|
277
|
+
"agent_id": f"{agent_type}_{session_id}",
|
|
278
|
+
"session_id": session_id,
|
|
279
|
+
"delegation_context": {
|
|
280
|
+
"description": tool_input.get("description", ""),
|
|
281
|
+
"timestamp": datetime.now().isoformat(),
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
# Log the agent prompt asynchronously
|
|
286
|
+
try:
|
|
287
|
+
loop = asyncio.get_running_loop()
|
|
288
|
+
asyncio.create_task(
|
|
289
|
+
log_manager.log_prompt(
|
|
290
|
+
f"agent_{agent_type}", prompt_content, metadata
|
|
291
|
+
)
|
|
292
|
+
)
|
|
293
|
+
except RuntimeError:
|
|
294
|
+
# No running loop, create one
|
|
295
|
+
loop = asyncio.new_event_loop()
|
|
296
|
+
asyncio.set_event_loop(loop)
|
|
297
|
+
loop.run_until_complete(
|
|
298
|
+
log_manager.log_prompt(
|
|
299
|
+
f"agent_{agent_type}", prompt_content, metadata
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
if DEBUG:
|
|
304
|
+
print(f" - Agent prompt logged for {agent_type}", file=sys.stderr)
|
|
305
|
+
except Exception as e:
|
|
306
|
+
if DEBUG:
|
|
307
|
+
print(f" - Could not log agent prompt: {e}", file=sys.stderr)
|
|
308
|
+
|
|
309
|
+
def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
|
|
261
310
|
"""Get git branch for the given directory with caching."""
|
|
262
311
|
# Use current working directory if not specified
|
|
263
312
|
if not working_dir:
|
|
@@ -285,7 +334,8 @@ class EventHandlers:
|
|
|
285
334
|
["git", "branch", "--show-current"],
|
|
286
335
|
capture_output=True,
|
|
287
336
|
text=True,
|
|
288
|
-
timeout=TimeoutConfig.QUICK_TIMEOUT,
|
|
337
|
+
timeout=TimeoutConfig.QUICK_TIMEOUT,
|
|
338
|
+
check=False, # Quick timeout to avoid hanging
|
|
289
339
|
)
|
|
290
340
|
|
|
291
341
|
# Restore original directory
|
|
@@ -297,11 +347,10 @@ class EventHandlers:
|
|
|
297
347
|
self.hook_handler._git_branch_cache[cache_key] = branch
|
|
298
348
|
self.hook_handler._git_branch_cache_time[cache_key] = current_time
|
|
299
349
|
return branch
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return "Unknown"
|
|
350
|
+
# Not a git repository or no branch
|
|
351
|
+
self.hook_handler._git_branch_cache[cache_key] = "Unknown"
|
|
352
|
+
self.hook_handler._git_branch_cache_time[cache_key] = current_time
|
|
353
|
+
return "Unknown"
|
|
305
354
|
|
|
306
355
|
except (
|
|
307
356
|
subprocess.TimeoutExpired,
|
|
@@ -339,11 +388,11 @@ class EventHandlers:
|
|
|
339
388
|
"tool_name": tool_name,
|
|
340
389
|
"exit_code": exit_code,
|
|
341
390
|
"success": exit_code == 0,
|
|
342
|
-
"status":
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
391
|
+
"status": (
|
|
392
|
+
"success"
|
|
393
|
+
if exit_code == 0
|
|
394
|
+
else "blocked" if exit_code == 2 else "error"
|
|
395
|
+
),
|
|
347
396
|
"duration_ms": duration,
|
|
348
397
|
"result_summary": result_data,
|
|
349
398
|
"session_id": event.get("session_id", ""),
|
|
@@ -412,9 +461,7 @@ class EventHandlers:
|
|
|
412
461
|
}
|
|
413
462
|
|
|
414
463
|
# Emit normalized event
|
|
415
|
-
self.hook_handler._emit_socketio_event(
|
|
416
|
-
"", "notification", notification_data
|
|
417
|
-
)
|
|
464
|
+
self.hook_handler._emit_socketio_event("", "notification", notification_data)
|
|
418
465
|
|
|
419
466
|
def handle_stop_fast(self, event):
|
|
420
467
|
"""Handle stop events when Claude processing stops.
|
|
@@ -448,9 +495,9 @@ class EventHandlers:
|
|
|
448
495
|
return {
|
|
449
496
|
"timestamp": datetime.now().isoformat(),
|
|
450
497
|
"working_directory": working_dir,
|
|
451
|
-
"git_branch":
|
|
452
|
-
|
|
453
|
-
|
|
498
|
+
"git_branch": (
|
|
499
|
+
self._get_git_branch(working_dir) if working_dir else "Unknown"
|
|
500
|
+
),
|
|
454
501
|
"event_type": "stop",
|
|
455
502
|
"reason": event.get("reason", "unknown"),
|
|
456
503
|
"stop_type": event.get("stop_type", "normal"),
|
|
@@ -612,9 +659,7 @@ class EventHandlers:
|
|
|
612
659
|
)
|
|
613
660
|
|
|
614
661
|
# Emit normalized event with high priority
|
|
615
|
-
self.hook_handler._emit_socketio_event(
|
|
616
|
-
"", "subagent_stop", subagent_stop_data
|
|
617
|
-
)
|
|
662
|
+
self.hook_handler._emit_socketio_event("", "subagent_stop", subagent_stop_data)
|
|
618
663
|
|
|
619
664
|
def _handle_subagent_response_tracking(
|
|
620
665
|
self,
|
|
@@ -665,9 +710,9 @@ class EventHandlers:
|
|
|
665
710
|
)
|
|
666
711
|
# Update the key to use the current session_id for consistency
|
|
667
712
|
if request_info:
|
|
668
|
-
self.hook_handler.delegation_requests[
|
|
669
|
-
|
|
670
|
-
|
|
713
|
+
self.hook_handler.delegation_requests[session_id] = (
|
|
714
|
+
request_info
|
|
715
|
+
)
|
|
671
716
|
# Optionally remove the old key to avoid duplicates
|
|
672
717
|
if stored_sid != session_id:
|
|
673
718
|
del self.hook_handler.delegation_requests[stored_sid]
|
|
@@ -750,7 +795,7 @@ class EventHandlers:
|
|
|
750
795
|
|
|
751
796
|
def handle_assistant_response(self, event):
|
|
752
797
|
"""Handle assistant response events for comprehensive response tracking.
|
|
753
|
-
|
|
798
|
+
|
|
754
799
|
WHY emit assistant response events:
|
|
755
800
|
- Provides visibility into Claude's responses to user prompts
|
|
756
801
|
- Captures response content and metadata for analysis
|
|
@@ -761,19 +806,21 @@ class EventHandlers:
|
|
|
761
806
|
self.hook_handler.response_tracking_manager.track_assistant_response(
|
|
762
807
|
event, self.hook_handler.pending_prompts
|
|
763
808
|
)
|
|
764
|
-
|
|
809
|
+
|
|
765
810
|
# Get working directory and git branch
|
|
766
811
|
working_dir = event.get("cwd", "")
|
|
767
812
|
git_branch = self._get_git_branch(working_dir) if working_dir else "Unknown"
|
|
768
|
-
|
|
813
|
+
|
|
769
814
|
# Extract response data
|
|
770
815
|
response_text = event.get("response", "")
|
|
771
816
|
session_id = event.get("session_id", "")
|
|
772
|
-
|
|
817
|
+
|
|
773
818
|
# Prepare assistant response data for Socket.IO emission
|
|
774
819
|
assistant_response_data = {
|
|
775
820
|
"response_text": response_text,
|
|
776
|
-
"response_preview":
|
|
821
|
+
"response_preview": (
|
|
822
|
+
response_text[:500] if len(response_text) > 500 else response_text
|
|
823
|
+
),
|
|
777
824
|
"response_length": len(response_text),
|
|
778
825
|
"session_id": session_id,
|
|
779
826
|
"working_directory": working_dir,
|
|
@@ -782,24 +829,32 @@ class EventHandlers:
|
|
|
782
829
|
"contains_code": "```" in response_text,
|
|
783
830
|
"contains_json": "```json" in response_text,
|
|
784
831
|
"hook_event_name": "AssistantResponse", # Explicitly set for dashboard
|
|
785
|
-
"has_structured_response": bool(
|
|
832
|
+
"has_structured_response": bool(
|
|
833
|
+
re.search(r"```json\s*\{.*?\}\s*```", response_text, re.DOTALL)
|
|
834
|
+
),
|
|
786
835
|
}
|
|
787
|
-
|
|
836
|
+
|
|
788
837
|
# Check if this is a response to a tracked prompt
|
|
789
838
|
if session_id in self.hook_handler.pending_prompts:
|
|
790
839
|
prompt_data = self.hook_handler.pending_prompts[session_id]
|
|
791
|
-
assistant_response_data["original_prompt"] = prompt_data.get("prompt", "")[
|
|
792
|
-
|
|
840
|
+
assistant_response_data["original_prompt"] = prompt_data.get("prompt", "")[
|
|
841
|
+
:200
|
|
842
|
+
]
|
|
843
|
+
assistant_response_data["prompt_timestamp"] = prompt_data.get(
|
|
844
|
+
"timestamp", ""
|
|
845
|
+
)
|
|
793
846
|
assistant_response_data["is_tracked_response"] = True
|
|
794
847
|
else:
|
|
795
848
|
assistant_response_data["is_tracked_response"] = False
|
|
796
|
-
|
|
849
|
+
|
|
797
850
|
# Debug logging
|
|
798
851
|
if DEBUG:
|
|
799
852
|
print(
|
|
800
853
|
f"Hook handler: Processing AssistantResponse - session: '{session_id}', response_length: {len(response_text)}",
|
|
801
854
|
file=sys.stderr,
|
|
802
855
|
)
|
|
803
|
-
|
|
856
|
+
|
|
804
857
|
# Emit normalized event
|
|
805
|
-
self.hook_handler._emit_socketio_event(
|
|
858
|
+
self.hook_handler._emit_socketio_event(
|
|
859
|
+
"", "assistant_response", assistant_response_data
|
|
860
|
+
)
|
|
@@ -12,7 +12,6 @@ WHY connection pooling approach:
|
|
|
12
12
|
- Falls back gracefully when Socket.IO unavailable
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
import atexit
|
|
16
15
|
import json
|
|
17
16
|
import os
|
|
18
17
|
import select
|
|
@@ -23,21 +22,34 @@ import threading
|
|
|
23
22
|
import time
|
|
24
23
|
from collections import deque
|
|
25
24
|
from datetime import datetime
|
|
25
|
+
from typing import Optional
|
|
26
26
|
|
|
27
27
|
# Import extracted modules with fallback for direct execution
|
|
28
28
|
try:
|
|
29
29
|
# Try relative imports first (when imported as module)
|
|
30
|
-
|
|
30
|
+
# Use the modern SocketIOConnectionPool instead of the deprecated local one
|
|
31
|
+
from claude_mpm.core.socketio_pool import get_connection_pool
|
|
32
|
+
|
|
31
33
|
from .event_handlers import EventHandlers
|
|
32
34
|
from .memory_integration import MemoryHookManager
|
|
33
35
|
from .response_tracking import ResponseTrackingManager
|
|
34
36
|
except ImportError:
|
|
35
37
|
# Fall back to absolute imports (when run directly)
|
|
36
|
-
import sys
|
|
37
38
|
from pathlib import Path
|
|
39
|
+
|
|
38
40
|
# Add parent directory to path
|
|
39
41
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
# Try to import get_connection_pool from deprecated location
|
|
44
|
+
try:
|
|
45
|
+
from connection_pool import SocketIOConnectionPool
|
|
46
|
+
|
|
47
|
+
def get_connection_pool():
|
|
48
|
+
return SocketIOConnectionPool()
|
|
49
|
+
|
|
50
|
+
except ImportError:
|
|
51
|
+
get_connection_pool = None
|
|
52
|
+
|
|
41
53
|
from event_handlers import EventHandlers
|
|
42
54
|
from memory_integration import MemoryHookManager
|
|
43
55
|
from response_tracking import ResponseTrackingManager
|
|
@@ -50,19 +62,27 @@ except ImportError:
|
|
|
50
62
|
class EventNormalizer:
|
|
51
63
|
def normalize(self, event_data):
|
|
52
64
|
"""Simple fallback normalizer that returns event as-is."""
|
|
53
|
-
return type(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
return type(
|
|
66
|
+
"NormalizedEvent",
|
|
67
|
+
(),
|
|
68
|
+
{
|
|
69
|
+
"to_dict": lambda: {
|
|
70
|
+
"event": "claude_event",
|
|
71
|
+
"type": event_data.get("type", "unknown"),
|
|
72
|
+
"subtype": event_data.get("subtype", "generic"),
|
|
73
|
+
"timestamp": event_data.get(
|
|
74
|
+
"timestamp", datetime.now().isoformat()
|
|
75
|
+
),
|
|
76
|
+
"data": event_data.get("data", event_data),
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
|
|
62
81
|
|
|
63
82
|
# Import EventBus for decoupled event distribution
|
|
64
83
|
try:
|
|
65
84
|
from claude_mpm.services.event_bus import EventBus
|
|
85
|
+
|
|
66
86
|
EVENTBUS_AVAILABLE = True
|
|
67
87
|
except ImportError:
|
|
68
88
|
EVENTBUS_AVAILABLE = False
|
|
@@ -124,8 +144,23 @@ class ClaudeHookHandler:
|
|
|
124
144
|
self.last_cleanup = time.time()
|
|
125
145
|
# Event normalizer for consistent event schema
|
|
126
146
|
self.event_normalizer = EventNormalizer()
|
|
127
|
-
|
|
128
|
-
# Initialize
|
|
147
|
+
|
|
148
|
+
# Initialize SocketIO connection pool for inter-process communication
|
|
149
|
+
# This sends events directly to the Socket.IO server in the daemon process
|
|
150
|
+
self.connection_pool = None
|
|
151
|
+
try:
|
|
152
|
+
self.connection_pool = get_connection_pool()
|
|
153
|
+
if DEBUG:
|
|
154
|
+
print("✅ Modern SocketIO connection pool initialized", file=sys.stderr)
|
|
155
|
+
except Exception as e:
|
|
156
|
+
if DEBUG:
|
|
157
|
+
print(
|
|
158
|
+
f"⚠️ Failed to initialize SocketIO connection pool: {e}",
|
|
159
|
+
file=sys.stderr,
|
|
160
|
+
)
|
|
161
|
+
self.connection_pool = None
|
|
162
|
+
|
|
163
|
+
# Initialize EventBus for in-process event distribution (optional)
|
|
129
164
|
self.event_bus = None
|
|
130
165
|
if EVENTBUS_AVAILABLE:
|
|
131
166
|
try:
|
|
@@ -164,7 +199,7 @@ class ClaudeHookHandler:
|
|
|
164
199
|
self.pending_prompts = {} # session_id -> prompt data
|
|
165
200
|
|
|
166
201
|
def _track_delegation(
|
|
167
|
-
self, session_id: str, agent_type: str, request_data: dict = None
|
|
202
|
+
self, session_id: str, agent_type: str, request_data: Optional[dict] = None
|
|
168
203
|
):
|
|
169
204
|
"""Track a new agent delegation with optional request data for response correlation."""
|
|
170
205
|
if DEBUG:
|
|
@@ -224,7 +259,7 @@ class ClaudeHookHandler:
|
|
|
224
259
|
|
|
225
260
|
def _cleanup_old_entries(self):
|
|
226
261
|
"""Clean up old entries to prevent memory growth."""
|
|
227
|
-
|
|
262
|
+
datetime.now().timestamp() - self.MAX_CACHE_AGE_SECONDS
|
|
228
263
|
|
|
229
264
|
# Clean up delegation tracking dictionaries
|
|
230
265
|
for storage in [self.active_delegations, self.delegation_requests]:
|
|
@@ -266,7 +301,7 @@ class ClaudeHookHandler:
|
|
|
266
301
|
|
|
267
302
|
return "unknown"
|
|
268
303
|
|
|
269
|
-
def _get_git_branch(self, working_dir: str = None) -> str:
|
|
304
|
+
def _get_git_branch(self, working_dir: Optional[str] = None) -> str:
|
|
270
305
|
"""Get git branch for the given directory with caching.
|
|
271
306
|
|
|
272
307
|
WHY caching approach:
|
|
@@ -301,7 +336,8 @@ class ClaudeHookHandler:
|
|
|
301
336
|
["git", "branch", "--show-current"],
|
|
302
337
|
capture_output=True,
|
|
303
338
|
text=True,
|
|
304
|
-
timeout=TimeoutConfig.QUICK_TIMEOUT,
|
|
339
|
+
timeout=TimeoutConfig.QUICK_TIMEOUT,
|
|
340
|
+
check=False, # Quick timeout to avoid hanging
|
|
305
341
|
)
|
|
306
342
|
|
|
307
343
|
# Restore original directory
|
|
@@ -313,11 +349,10 @@ class ClaudeHookHandler:
|
|
|
313
349
|
self._git_branch_cache[cache_key] = branch
|
|
314
350
|
self._git_branch_cache_time[cache_key] = current_time
|
|
315
351
|
return branch
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
return "Unknown"
|
|
352
|
+
# Not a git repository or no branch
|
|
353
|
+
self._git_branch_cache[cache_key] = "Unknown"
|
|
354
|
+
self._git_branch_cache_time[cache_key] = current_time
|
|
355
|
+
return "Unknown"
|
|
321
356
|
|
|
322
357
|
except (
|
|
323
358
|
subprocess.TimeoutExpired,
|
|
@@ -366,31 +401,37 @@ class ClaudeHookHandler:
|
|
|
366
401
|
self._continue_execution()
|
|
367
402
|
_continue_sent = True
|
|
368
403
|
return
|
|
369
|
-
|
|
404
|
+
|
|
370
405
|
# Check for duplicate events (same event within 100ms)
|
|
371
406
|
global _recent_events, _events_lock
|
|
372
407
|
event_key = self._get_event_key(event)
|
|
373
408
|
current_time = time.time()
|
|
374
|
-
|
|
409
|
+
|
|
375
410
|
with _events_lock:
|
|
376
411
|
# Check if we've seen this event recently
|
|
377
412
|
for recent_key, recent_time in _recent_events:
|
|
378
413
|
if recent_key == event_key and (current_time - recent_time) < 0.1:
|
|
379
414
|
if DEBUG:
|
|
380
|
-
print(
|
|
415
|
+
print(
|
|
416
|
+
f"[{datetime.now().isoformat()}] Skipping duplicate event: {event.get('hook_event_name', 'unknown')} (PID: {os.getpid()})",
|
|
417
|
+
file=sys.stderr,
|
|
418
|
+
)
|
|
381
419
|
# Still need to output continue for this invocation
|
|
382
420
|
if not _continue_sent:
|
|
383
421
|
self._continue_execution()
|
|
384
422
|
_continue_sent = True
|
|
385
423
|
return
|
|
386
|
-
|
|
424
|
+
|
|
387
425
|
# Not a duplicate, record it
|
|
388
426
|
_recent_events.append((event_key, current_time))
|
|
389
|
-
|
|
427
|
+
|
|
390
428
|
# Debug: Log that we're processing an event
|
|
391
429
|
if DEBUG:
|
|
392
430
|
hook_type = event.get("hook_event_name", "unknown")
|
|
393
|
-
print(
|
|
431
|
+
print(
|
|
432
|
+
f"\n[{datetime.now().isoformat()}] Processing hook event: {hook_type} (PID: {os.getpid()})",
|
|
433
|
+
file=sys.stderr,
|
|
434
|
+
)
|
|
394
435
|
|
|
395
436
|
# Increment event counter and perform periodic cleanup
|
|
396
437
|
self.events_processed += 1
|
|
@@ -493,7 +534,7 @@ class ClaudeHookHandler:
|
|
|
493
534
|
|
|
494
535
|
def _get_event_key(self, event: dict) -> str:
|
|
495
536
|
"""Generate a unique key for an event to detect duplicates.
|
|
496
|
-
|
|
537
|
+
|
|
497
538
|
WHY: Claude Code may call the hook multiple times for the same event
|
|
498
539
|
because the hook is registered for multiple event types. We need to
|
|
499
540
|
detect and skip duplicate processing while still returning continue.
|
|
@@ -501,7 +542,7 @@ class ClaudeHookHandler:
|
|
|
501
542
|
# Create a key from event type, session_id, and key data
|
|
502
543
|
hook_type = event.get("hook_event_name", "unknown")
|
|
503
544
|
session_id = event.get("session_id", "")
|
|
504
|
-
|
|
545
|
+
|
|
505
546
|
# Add type-specific data to make the key unique
|
|
506
547
|
if hook_type == "PreToolUse":
|
|
507
548
|
tool_name = event.get("tool_name", "")
|
|
@@ -509,17 +550,17 @@ class ClaudeHookHandler:
|
|
|
509
550
|
if tool_name == "Task":
|
|
510
551
|
tool_input = event.get("tool_input", {})
|
|
511
552
|
agent = tool_input.get("subagent_type", "")
|
|
512
|
-
prompt_preview = (
|
|
553
|
+
prompt_preview = (
|
|
554
|
+
tool_input.get("prompt", "") or tool_input.get("description", "")
|
|
555
|
+
)[:50]
|
|
513
556
|
return f"{hook_type}:{session_id}:{tool_name}:{agent}:{prompt_preview}"
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
elif hook_type == "UserPromptSubmit":
|
|
557
|
+
return f"{hook_type}:{session_id}:{tool_name}"
|
|
558
|
+
if hook_type == "UserPromptSubmit":
|
|
517
559
|
prompt_preview = event.get("prompt", "")[:50]
|
|
518
560
|
return f"{hook_type}:{session_id}:{prompt_preview}"
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
561
|
+
# For other events, just use type and session
|
|
562
|
+
return f"{hook_type}:{session_id}"
|
|
563
|
+
|
|
523
564
|
def _continue_execution(self) -> None:
|
|
524
565
|
"""
|
|
525
566
|
Send continue action to Claude.
|
|
@@ -529,16 +570,13 @@ class ClaudeHookHandler:
|
|
|
529
570
|
"""
|
|
530
571
|
print(json.dumps({"action": "continue"}))
|
|
531
572
|
|
|
532
|
-
|
|
533
573
|
def _emit_socketio_event(self, namespace: str, event: str, data: dict):
|
|
534
|
-
"""Emit event through
|
|
535
|
-
|
|
536
|
-
WHY
|
|
537
|
-
-
|
|
538
|
-
- EventBus
|
|
539
|
-
-
|
|
540
|
-
- More resilient with centralized failure handling
|
|
541
|
-
- Cleaner architecture and easier testing
|
|
574
|
+
"""Emit event through both connection pool and EventBus.
|
|
575
|
+
|
|
576
|
+
WHY dual approach:
|
|
577
|
+
- Connection pool: Direct Socket.IO connection for inter-process communication
|
|
578
|
+
- EventBus: For in-process subscribers (if any)
|
|
579
|
+
- Ensures events reach the dashboard regardless of process boundaries
|
|
542
580
|
"""
|
|
543
581
|
# Create event data for normalization
|
|
544
582
|
raw_event = {
|
|
@@ -549,7 +587,7 @@ class ClaudeHookHandler:
|
|
|
549
587
|
"source": "claude_hooks", # Identify the source
|
|
550
588
|
"session_id": data.get("sessionId"), # Include session if available
|
|
551
589
|
}
|
|
552
|
-
|
|
590
|
+
|
|
553
591
|
# Normalize the event using EventNormalizer for consistent schema
|
|
554
592
|
normalized_event = self.event_normalizer.normalize(raw_event, source="hook")
|
|
555
593
|
claude_event_data = normalized_event.to_dict()
|
|
@@ -569,8 +607,20 @@ class ClaudeHookHandler:
|
|
|
569
607
|
f"Hook handler: Publishing Task delegation to agent '{agent_type}'",
|
|
570
608
|
file=sys.stderr,
|
|
571
609
|
)
|
|
572
|
-
|
|
573
|
-
#
|
|
610
|
+
|
|
611
|
+
# First, try to emit through direct Socket.IO connection pool
|
|
612
|
+
# This is the primary path for inter-process communication
|
|
613
|
+
if self.connection_pool:
|
|
614
|
+
try:
|
|
615
|
+
# Emit to Socket.IO server directly
|
|
616
|
+
self.connection_pool.emit("claude_event", claude_event_data)
|
|
617
|
+
if DEBUG:
|
|
618
|
+
print(f"✅ Emitted via connection pool: {event}", file=sys.stderr)
|
|
619
|
+
except Exception as e:
|
|
620
|
+
if DEBUG:
|
|
621
|
+
print(f"⚠️ Failed to emit via connection pool: {e}", file=sys.stderr)
|
|
622
|
+
|
|
623
|
+
# Also publish to EventBus for any in-process subscribers
|
|
574
624
|
if self.event_bus and EVENTBUS_AVAILABLE:
|
|
575
625
|
try:
|
|
576
626
|
# Publish to EventBus with topic format: hook.{event}
|
|
@@ -581,9 +631,10 @@ class ClaudeHookHandler:
|
|
|
581
631
|
except Exception as e:
|
|
582
632
|
if DEBUG:
|
|
583
633
|
print(f"⚠️ Failed to publish to EventBus: {e}", file=sys.stderr)
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
634
|
+
|
|
635
|
+
# Warn if neither method is available
|
|
636
|
+
if not self.connection_pool and not self.event_bus and DEBUG:
|
|
637
|
+
print(f"⚠️ No event emission method available for: {event}", file=sys.stderr)
|
|
587
638
|
|
|
588
639
|
def handle_subagent_stop(self, event: dict):
|
|
589
640
|
"""Handle subagent stop events with improved agent type detection.
|
|
@@ -609,16 +660,14 @@ class ClaudeHookHandler:
|
|
|
609
660
|
# Show all stored session IDs for comparison
|
|
610
661
|
all_sessions = list(self.delegation_requests.keys())
|
|
611
662
|
if all_sessions:
|
|
612
|
-
print(
|
|
663
|
+
print(" - Stored sessions (first 16 chars):", file=sys.stderr)
|
|
613
664
|
for sid in all_sessions[:10]: # Show up to 10
|
|
614
665
|
print(
|
|
615
666
|
f" - {sid[:16]}... (agent: {self.delegation_requests[sid].get('agent_type', 'unknown')})",
|
|
616
667
|
file=sys.stderr,
|
|
617
668
|
)
|
|
618
669
|
else:
|
|
619
|
-
print(
|
|
620
|
-
f" - No stored sessions in delegation_requests!", file=sys.stderr
|
|
621
|
-
)
|
|
670
|
+
print(" - No stored sessions in delegation_requests!", file=sys.stderr)
|
|
622
671
|
|
|
623
672
|
# First try to get agent type from our tracking
|
|
624
673
|
agent_type = (
|
|
@@ -691,16 +740,16 @@ class ClaudeHookHandler:
|
|
|
691
740
|
print(f" - reason: {reason}", file=sys.stderr)
|
|
692
741
|
# Check if session exists in our storage
|
|
693
742
|
if session_id in self.delegation_requests:
|
|
694
|
-
print(
|
|
743
|
+
print(" - ✅ Session found in delegation_requests", file=sys.stderr)
|
|
695
744
|
print(
|
|
696
745
|
f" - Stored agent: {self.delegation_requests[session_id].get('agent_type')}",
|
|
697
746
|
file=sys.stderr,
|
|
698
747
|
)
|
|
699
748
|
else:
|
|
700
749
|
print(
|
|
701
|
-
|
|
750
|
+
" - ❌ Session NOT found in delegation_requests!", file=sys.stderr
|
|
702
751
|
)
|
|
703
|
-
print(
|
|
752
|
+
print(" - Looking for partial match...", file=sys.stderr)
|
|
704
753
|
# Try to find partial matches
|
|
705
754
|
for stored_sid in list(self.delegation_requests.keys())[:10]:
|
|
706
755
|
if stored_sid.startswith(session_id[:8]) or session_id.startswith(
|
|
@@ -758,7 +807,7 @@ class ClaudeHookHandler:
|
|
|
758
807
|
)
|
|
759
808
|
if request_info:
|
|
760
809
|
print(
|
|
761
|
-
|
|
810
|
+
" - ✅ Found request data for response tracking",
|
|
762
811
|
file=sys.stderr,
|
|
763
812
|
)
|
|
764
813
|
print(
|
|
@@ -820,9 +869,9 @@ class ClaudeHookHandler:
|
|
|
820
869
|
metadata["task_completed"] = structured_response.get(
|
|
821
870
|
"task_completed", False
|
|
822
871
|
)
|
|
823
|
-
|
|
872
|
+
|
|
824
873
|
# Check for MEMORIES field and process if present
|
|
825
|
-
if
|
|
874
|
+
if structured_response.get("MEMORIES"):
|
|
826
875
|
memories = structured_response["MEMORIES"]
|
|
827
876
|
if DEBUG:
|
|
828
877
|
print(
|
|
@@ -892,11 +941,13 @@ class ClaudeHookHandler:
|
|
|
892
941
|
"files_modified": structured_response.get("files_modified", []),
|
|
893
942
|
"tools_used": structured_response.get("tools_used", []),
|
|
894
943
|
"remember": structured_response.get("remember"),
|
|
895
|
-
"MEMORIES": structured_response.get(
|
|
944
|
+
"MEMORIES": structured_response.get(
|
|
945
|
+
"MEMORIES"
|
|
946
|
+
), # Complete memory replacement
|
|
896
947
|
}
|
|
897
|
-
|
|
948
|
+
|
|
898
949
|
# Log if MEMORIES field is present
|
|
899
|
-
if
|
|
950
|
+
if structured_response.get("MEMORIES"):
|
|
900
951
|
if DEBUG:
|
|
901
952
|
memories_count = len(structured_response["MEMORIES"])
|
|
902
953
|
print(
|
|
@@ -916,8 +967,12 @@ class ClaudeHookHandler:
|
|
|
916
967
|
|
|
917
968
|
def __del__(self):
|
|
918
969
|
"""Cleanup on handler destruction."""
|
|
919
|
-
#
|
|
920
|
-
|
|
970
|
+
# Clean up connection pool if it exists
|
|
971
|
+
if hasattr(self, "connection_pool") and self.connection_pool:
|
|
972
|
+
try:
|
|
973
|
+
self.connection_pool.cleanup()
|
|
974
|
+
except:
|
|
975
|
+
pass # Ignore cleanup errors during destruction
|
|
921
976
|
|
|
922
977
|
|
|
923
978
|
def main():
|
|
@@ -954,12 +1009,11 @@ def main():
|
|
|
954
1009
|
f"✅ Created new ClaudeHookHandler singleton (pid: {os.getpid()})",
|
|
955
1010
|
file=sys.stderr,
|
|
956
1011
|
)
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
)
|
|
1012
|
+
elif DEBUG:
|
|
1013
|
+
print(
|
|
1014
|
+
f"♻️ Reusing existing ClaudeHookHandler singleton (pid: {os.getpid()})",
|
|
1015
|
+
file=sys.stderr,
|
|
1016
|
+
)
|
|
963
1017
|
|
|
964
1018
|
handler = _global_handler
|
|
965
1019
|
|