claude-mpm 4.1.1__py3-none-any.whl → 4.1.3__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/engineer.json +33 -11
- 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 +50 -2
- 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 +648 -1098
- 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 +339 -967
- claude_mpm/cli/commands/monitor.py +117 -88
- claude_mpm/cli/commands/run.py +233 -542
- 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 +280 -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 +22 -29
- 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 +500 -680
- 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 -17
- 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 +99 -154
- claude_mpm/hooks/claude_hooks/hook_handler.py +110 -720
- claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +104 -77
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- 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/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- 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 +129 -511
- 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/base_agent_locator.py +132 -0
- 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_results_manager.py +185 -0
- 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/single_agent_deployer.py +315 -0
- 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 +157 -503
- 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/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/memory/template_generator.py +4 -6
- claude_mpm/services/agents/registry/__init__.py +11 -7
- 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/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- 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/cache_manager.py +311 -0
- 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/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- 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 +152 -97
- 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.1.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
- claude_mpm-4.1.3.dist-info/RECORD +528 -0
- claude_mpm/cli/commands/run_config_checker.py +0 -160
- claude_mpm-4.1.1.dist-info/RECORD +0 -494
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
|
@@ -19,16 +19,15 @@ import asyncio
|
|
|
19
19
|
import logging
|
|
20
20
|
import os
|
|
21
21
|
import signal
|
|
22
|
-
import threading
|
|
23
22
|
import time
|
|
24
23
|
from abc import ABC, abstractmethod
|
|
25
24
|
from collections import deque
|
|
26
25
|
from dataclasses import dataclass
|
|
27
26
|
from datetime import datetime, timezone
|
|
28
27
|
from enum import Enum
|
|
29
|
-
from typing import Any, Callable, Dict, List, Optional
|
|
28
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
30
29
|
|
|
31
|
-
from claude_mpm.core.constants import PerformanceConfig, RetryConfig
|
|
30
|
+
from claude_mpm.core.constants import PerformanceConfig, RetryConfig
|
|
32
31
|
|
|
33
32
|
from .infrastructure.monitoring import HealthCheckResult, HealthStatus
|
|
34
33
|
|
|
@@ -85,17 +84,14 @@ class RecoveryStrategy(ABC):
|
|
|
85
84
|
@abstractmethod
|
|
86
85
|
def should_recover(self, health_result: HealthCheckResult) -> bool:
|
|
87
86
|
"""Determine if recovery should be triggered based on health result."""
|
|
88
|
-
pass
|
|
89
87
|
|
|
90
88
|
@abstractmethod
|
|
91
89
|
def get_recovery_action(self, health_result: HealthCheckResult) -> RecoveryAction:
|
|
92
90
|
"""Determine the appropriate recovery action."""
|
|
93
|
-
pass
|
|
94
91
|
|
|
95
92
|
@abstractmethod
|
|
96
93
|
def get_name(self) -> str:
|
|
97
94
|
"""Get the name of this recovery strategy."""
|
|
98
|
-
pass
|
|
99
95
|
|
|
100
96
|
|
|
101
97
|
class GradedRecoveryStrategy(RecoveryStrategy):
|
|
@@ -183,16 +179,14 @@ class GradedRecoveryStrategy(RecoveryStrategy):
|
|
|
183
179
|
if health_result.overall_status == HealthStatus.CRITICAL:
|
|
184
180
|
if failure_count >= 3:
|
|
185
181
|
return RecoveryAction.EMERGENCY_STOP
|
|
186
|
-
|
|
182
|
+
if failure_count >= 2:
|
|
187
183
|
return RecoveryAction.RESTART_SERVICE
|
|
188
|
-
|
|
189
|
-
return RecoveryAction.CLEAR_CONNECTIONS
|
|
184
|
+
return RecoveryAction.CLEAR_CONNECTIONS
|
|
190
185
|
|
|
191
|
-
|
|
186
|
+
if health_result.overall_status == HealthStatus.WARNING:
|
|
192
187
|
if failure_count >= self.warning_threshold:
|
|
193
188
|
return RecoveryAction.CLEAR_CONNECTIONS
|
|
194
|
-
|
|
195
|
-
return RecoveryAction.LOG_WARNING
|
|
189
|
+
return RecoveryAction.LOG_WARNING
|
|
196
190
|
|
|
197
191
|
return RecoveryAction.NONE
|
|
198
192
|
|
|
@@ -242,17 +236,14 @@ class CircuitBreaker:
|
|
|
242
236
|
if self.state == CircuitState.CLOSED:
|
|
243
237
|
return True
|
|
244
238
|
|
|
245
|
-
|
|
239
|
+
if self.state == CircuitState.OPEN:
|
|
246
240
|
# Check if timeout has elapsed
|
|
247
241
|
if current_time - self.last_failure_time >= self.timeout_seconds:
|
|
248
242
|
self._transition_to_half_open()
|
|
249
243
|
return True
|
|
250
244
|
return False
|
|
251
245
|
|
|
252
|
-
|
|
253
|
-
return True
|
|
254
|
-
|
|
255
|
-
return False
|
|
246
|
+
return self.state == CircuitState.HALF_OPEN
|
|
256
247
|
|
|
257
248
|
def record_success(self) -> None:
|
|
258
249
|
"""Record a successful recovery operation."""
|
|
@@ -612,8 +603,8 @@ class RecoveryManager:
|
|
|
612
603
|
self.logger.info("Attempting graceful service restart")
|
|
613
604
|
|
|
614
605
|
# Save current configuration
|
|
615
|
-
|
|
616
|
-
|
|
606
|
+
getattr(self.server_instance, "host", "localhost")
|
|
607
|
+
getattr(self.server_instance, "port", 8765)
|
|
617
608
|
|
|
618
609
|
# Stop current server
|
|
619
610
|
try:
|
|
@@ -24,7 +24,6 @@ from typing import Any, Dict, Optional
|
|
|
24
24
|
|
|
25
25
|
from claude_mpm.core.config import Config
|
|
26
26
|
from claude_mpm.core.shared.config_loader import ConfigLoader
|
|
27
|
-
from claude_mpm.services.claude_session_logger import ClaudeSessionLogger
|
|
28
27
|
|
|
29
28
|
logger = logging.getLogger(__name__)
|
|
30
29
|
|
|
@@ -17,9 +17,8 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
|
17
17
|
|
|
18
18
|
from claude_mpm.core.base_service import BaseService
|
|
19
19
|
from claude_mpm.core.config import Config
|
|
20
|
-
from claude_mpm.core.container import ServiceLifetime
|
|
20
|
+
from claude_mpm.core.container import ServiceLifetime
|
|
21
21
|
from claude_mpm.core.logger import get_project_logger
|
|
22
|
-
from claude_mpm.core.logging_config import get_logger
|
|
23
22
|
from claude_mpm.core.shared.config_loader import ConfigLoader
|
|
24
23
|
from claude_mpm.services.core.interfaces import RunnerConfigurationInterface
|
|
25
24
|
|
|
@@ -33,11 +32,9 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
33
32
|
|
|
34
33
|
async def _initialize(self) -> None:
|
|
35
34
|
"""Initialize the service. No special initialization needed."""
|
|
36
|
-
pass
|
|
37
35
|
|
|
38
36
|
async def _cleanup(self) -> None:
|
|
39
37
|
"""Cleanup service resources. No cleanup needed."""
|
|
40
|
-
pass
|
|
41
38
|
|
|
42
39
|
# Implementation of abstract methods from RunnerConfigurationInterface
|
|
43
40
|
|
|
@@ -61,7 +58,6 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
61
58
|
"""
|
|
62
59
|
# This method can delegate to existing service registration methods
|
|
63
60
|
# For now, this is a no-op as service registration is handled elsewhere
|
|
64
|
-
pass
|
|
65
61
|
|
|
66
62
|
def load_configuration(self, config_path: Optional[Path] = None) -> Dict[str, Any]:
|
|
67
63
|
"""Load configuration from file or defaults.
|
|
@@ -78,12 +74,15 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
78
74
|
if config_path:
|
|
79
75
|
# Use specific config file with ConfigLoader
|
|
80
76
|
from claude_mpm.core.shared.config_loader import ConfigPattern
|
|
77
|
+
|
|
81
78
|
pattern = ConfigPattern(
|
|
82
79
|
filenames=[Path(config_path).name],
|
|
83
80
|
search_paths=[str(Path(config_path).parent)],
|
|
84
|
-
env_prefix="CLAUDE_MPM_"
|
|
81
|
+
env_prefix="CLAUDE_MPM_",
|
|
82
|
+
)
|
|
83
|
+
config = config_loader.load_config(
|
|
84
|
+
pattern, cache_key=f"runner_{config_path}"
|
|
85
85
|
)
|
|
86
|
-
config = config_loader.load_config(pattern, cache_key=f"runner_{config_path}")
|
|
87
86
|
else:
|
|
88
87
|
# Use main config
|
|
89
88
|
config = config_loader.load_main_config()
|
|
@@ -198,7 +197,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
198
197
|
try:
|
|
199
198
|
project_logger = get_project_logger(log_level)
|
|
200
199
|
project_logger.log_system(
|
|
201
|
-
|
|
200
|
+
"Initializing ClaudeRunner", level="INFO", component="runner"
|
|
202
201
|
)
|
|
203
202
|
return project_logger
|
|
204
203
|
except ImportError as e:
|
|
@@ -231,7 +230,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
231
230
|
"Response logging initialized", level="INFO", component="logging"
|
|
232
231
|
)
|
|
233
232
|
return response_logger
|
|
234
|
-
except Exception
|
|
233
|
+
except Exception:
|
|
235
234
|
self.logger.warning("Failed to initialize response logger", exc_info=True)
|
|
236
235
|
return None
|
|
237
236
|
|
|
@@ -244,7 +243,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
244
243
|
if "CLAUDE_MPM_USER_PWD" in os.environ:
|
|
245
244
|
user_working_dir = Path(os.environ["CLAUDE_MPM_USER_PWD"])
|
|
246
245
|
self.logger.info(
|
|
247
|
-
|
|
246
|
+
"Using user working directory from CLAUDE_MPM_USER_PWD",
|
|
248
247
|
extra={"directory": str(user_working_dir)},
|
|
249
248
|
)
|
|
250
249
|
return user_working_dir
|
|
@@ -310,7 +309,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
310
309
|
|
|
311
310
|
try:
|
|
312
311
|
return container.get(HookServiceInterface)
|
|
313
|
-
except Exception
|
|
312
|
+
except Exception:
|
|
314
313
|
self.logger.warning("Failed to initialize hook service", exc_info=True)
|
|
315
314
|
return None
|
|
316
315
|
|
|
@@ -336,7 +335,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
336
335
|
|
|
337
336
|
try:
|
|
338
337
|
return container.get(AgentCapabilitiesInterface)
|
|
339
|
-
except Exception
|
|
338
|
+
except Exception:
|
|
340
339
|
self.logger.warning(
|
|
341
340
|
"Failed to initialize agent capabilities service", exc_info=True
|
|
342
341
|
)
|
|
@@ -371,7 +370,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
371
370
|
|
|
372
371
|
try:
|
|
373
372
|
return container.get(SystemInstructionsInterface)
|
|
374
|
-
except Exception
|
|
373
|
+
except Exception:
|
|
375
374
|
self.logger.warning(
|
|
376
375
|
"Failed to initialize system instructions service", exc_info=True
|
|
377
376
|
)
|
|
@@ -407,7 +406,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
407
406
|
|
|
408
407
|
try:
|
|
409
408
|
return container.get(SubprocessLauncherInterface)
|
|
410
|
-
except Exception
|
|
409
|
+
except Exception:
|
|
411
410
|
self.logger.warning(
|
|
412
411
|
"Failed to initialize subprocess launcher service", exc_info=True
|
|
413
412
|
)
|
|
@@ -431,7 +430,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
431
430
|
|
|
432
431
|
try:
|
|
433
432
|
return container.get(VersionServiceInterface)
|
|
434
|
-
except Exception
|
|
433
|
+
except Exception:
|
|
435
434
|
self.logger.warning("Failed to initialize version service", exc_info=True)
|
|
436
435
|
return None
|
|
437
436
|
|
|
@@ -460,7 +459,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
460
459
|
|
|
461
460
|
try:
|
|
462
461
|
return container.get(CommandHandlerInterface)
|
|
463
|
-
except Exception
|
|
462
|
+
except Exception:
|
|
464
463
|
self.logger.warning(
|
|
465
464
|
"Failed to initialize command handler service", exc_info=True
|
|
466
465
|
)
|
|
@@ -489,7 +488,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
489
488
|
|
|
490
489
|
try:
|
|
491
490
|
return container.get(MemoryHookInterface)
|
|
492
|
-
except Exception
|
|
491
|
+
except Exception:
|
|
493
492
|
self.logger.warning(
|
|
494
493
|
"Failed to initialize memory hook service", exc_info=True
|
|
495
494
|
)
|
|
@@ -520,7 +519,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
520
519
|
|
|
521
520
|
try:
|
|
522
521
|
return container.get(SessionManagementInterface)
|
|
523
|
-
except Exception
|
|
522
|
+
except Exception:
|
|
524
523
|
self.logger.warning(
|
|
525
524
|
"Failed to initialize session management service", exc_info=True
|
|
526
525
|
)
|
|
@@ -544,7 +543,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
544
543
|
|
|
545
544
|
try:
|
|
546
545
|
return container.get(UtilityServiceInterface)
|
|
547
|
-
except Exception
|
|
546
|
+
except Exception:
|
|
548
547
|
self.logger.warning("Failed to initialize utility service", exc_info=True)
|
|
549
548
|
return None
|
|
550
549
|
|
|
@@ -569,7 +568,7 @@ class RunnerConfigurationService(BaseService, RunnerConfigurationInterface):
|
|
|
569
568
|
session_log_file = project_logger.session_dir / "system.jsonl"
|
|
570
569
|
|
|
571
570
|
# Log session start event
|
|
572
|
-
|
|
571
|
+
{
|
|
573
572
|
"event": "session_start",
|
|
574
573
|
"runner": "ClaudeRunner",
|
|
575
574
|
"enable_tickets": config_data.get("enable_tickets"),
|
|
@@ -34,11 +34,9 @@ class SessionManagementService(BaseService, SessionManagementInterface):
|
|
|
34
34
|
|
|
35
35
|
async def _initialize(self) -> None:
|
|
36
36
|
"""Initialize the service. No special initialization needed."""
|
|
37
|
-
pass
|
|
38
37
|
|
|
39
38
|
async def _cleanup(self) -> None:
|
|
40
39
|
"""Cleanup service resources. No cleanup needed."""
|
|
41
|
-
pass
|
|
42
40
|
|
|
43
41
|
def run_interactive_session(self, initial_context: Optional[str] = None) -> bool:
|
|
44
42
|
"""Run Claude in interactive mode using session delegation.
|
|
@@ -252,9 +250,8 @@ class SessionManagementService(BaseService, SessionManagementInterface):
|
|
|
252
250
|
|
|
253
251
|
self.logger.info(f"Ended session {session_id}")
|
|
254
252
|
return True
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
return False
|
|
253
|
+
self.logger.warning(f"Session {session_id} not found")
|
|
254
|
+
return False
|
|
258
255
|
|
|
259
256
|
def get_session_status(self, session_id: str) -> Dict[str, Any]:
|
|
260
257
|
"""Get status of a session.
|
|
@@ -267,12 +264,11 @@ class SessionManagementService(BaseService, SessionManagementInterface):
|
|
|
267
264
|
"""
|
|
268
265
|
if session_id in self.active_sessions:
|
|
269
266
|
return self.active_sessions[session_id].copy()
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
267
|
+
return {
|
|
268
|
+
"id": session_id,
|
|
269
|
+
"status": "not_found",
|
|
270
|
+
"error": "Session not found",
|
|
271
|
+
}
|
|
276
272
|
|
|
277
273
|
def list_active_sessions(self) -> List[str]:
|
|
278
274
|
"""List all active session IDs.
|
|
@@ -12,6 +12,7 @@ from ...core.mixins import LoggerMixin
|
|
|
12
12
|
|
|
13
13
|
class AsyncServiceState(Enum):
|
|
14
14
|
"""Standard states for async services."""
|
|
15
|
+
|
|
15
16
|
UNINITIALIZED = "uninitialized"
|
|
16
17
|
INITIALIZING = "initializing"
|
|
17
18
|
RUNNING = "running"
|
|
@@ -23,18 +24,18 @@ class AsyncServiceState(Enum):
|
|
|
23
24
|
class AsyncServiceBase(LoggerMixin, ABC):
|
|
24
25
|
"""
|
|
25
26
|
Base class for asynchronous services.
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
Provides common patterns:
|
|
28
29
|
- State management
|
|
29
30
|
- Lifecycle methods
|
|
30
31
|
- Error handling
|
|
31
32
|
- Background task management
|
|
32
33
|
"""
|
|
33
|
-
|
|
34
|
+
|
|
34
35
|
def __init__(self, service_name: str, config: Optional[Dict[str, Any]] = None):
|
|
35
36
|
"""
|
|
36
37
|
Initialize async service.
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
Args:
|
|
39
40
|
service_name: Name of the service
|
|
40
41
|
config: Optional configuration dictionary
|
|
@@ -42,38 +43,38 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
42
43
|
self.service_name = service_name
|
|
43
44
|
self._logger_name = f"service.{service_name}"
|
|
44
45
|
self.config = config or {}
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
# State management
|
|
47
48
|
self._state = AsyncServiceState.UNINITIALIZED
|
|
48
49
|
self._state_lock = asyncio.Lock()
|
|
49
|
-
|
|
50
|
+
|
|
50
51
|
# Background tasks
|
|
51
52
|
self._background_tasks: set = set()
|
|
52
53
|
self._shutdown_event = asyncio.Event()
|
|
53
|
-
|
|
54
|
+
|
|
54
55
|
# Error tracking
|
|
55
56
|
self._last_error: Optional[Exception] = None
|
|
56
57
|
self._error_count = 0
|
|
57
|
-
|
|
58
|
+
|
|
58
59
|
@property
|
|
59
60
|
def state(self) -> AsyncServiceState:
|
|
60
61
|
"""Get current service state."""
|
|
61
62
|
return self._state
|
|
62
|
-
|
|
63
|
+
|
|
63
64
|
@property
|
|
64
65
|
def is_running(self) -> bool:
|
|
65
66
|
"""Check if service is running."""
|
|
66
67
|
return self._state == AsyncServiceState.RUNNING
|
|
67
|
-
|
|
68
|
+
|
|
68
69
|
@property
|
|
69
70
|
def is_healthy(self) -> bool:
|
|
70
71
|
"""Check if service is healthy."""
|
|
71
72
|
return self._state == AsyncServiceState.RUNNING and self._last_error is None
|
|
72
|
-
|
|
73
|
+
|
|
73
74
|
async def initialize(self) -> bool:
|
|
74
75
|
"""
|
|
75
76
|
Initialize the service.
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
Returns:
|
|
78
79
|
True if initialization successful
|
|
79
80
|
"""
|
|
@@ -81,113 +82,122 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
81
82
|
if self._state != AsyncServiceState.UNINITIALIZED:
|
|
82
83
|
self.logger.warning(f"Service {self.service_name} already initialized")
|
|
83
84
|
return self._state == AsyncServiceState.RUNNING
|
|
84
|
-
|
|
85
|
+
|
|
85
86
|
self._state = AsyncServiceState.INITIALIZING
|
|
86
87
|
self.logger.info(f"Initializing service: {self.service_name}")
|
|
87
|
-
|
|
88
|
+
|
|
88
89
|
try:
|
|
89
90
|
success = await self._do_initialize()
|
|
90
91
|
if success:
|
|
91
92
|
self._state = AsyncServiceState.RUNNING
|
|
92
|
-
self.logger.info(
|
|
93
|
+
self.logger.info(
|
|
94
|
+
f"Service {self.service_name} initialized successfully"
|
|
95
|
+
)
|
|
93
96
|
else:
|
|
94
97
|
self._state = AsyncServiceState.ERROR
|
|
95
|
-
self.logger.error(
|
|
96
|
-
|
|
98
|
+
self.logger.error(
|
|
99
|
+
f"Service {self.service_name} initialization failed"
|
|
100
|
+
)
|
|
101
|
+
|
|
97
102
|
return success
|
|
98
|
-
|
|
103
|
+
|
|
99
104
|
except Exception as e:
|
|
100
105
|
self._state = AsyncServiceState.ERROR
|
|
101
106
|
self._last_error = e
|
|
102
107
|
self._error_count += 1
|
|
103
|
-
self.logger.error(
|
|
108
|
+
self.logger.error(
|
|
109
|
+
f"Service {self.service_name} initialization error: {e}",
|
|
110
|
+
exc_info=True,
|
|
111
|
+
)
|
|
104
112
|
return False
|
|
105
|
-
|
|
113
|
+
|
|
106
114
|
async def shutdown(self) -> None:
|
|
107
115
|
"""Shutdown the service gracefully."""
|
|
108
116
|
async with self._state_lock:
|
|
109
117
|
if self._state in (AsyncServiceState.STOPPED, AsyncServiceState.STOPPING):
|
|
110
118
|
return
|
|
111
|
-
|
|
119
|
+
|
|
112
120
|
self._state = AsyncServiceState.STOPPING
|
|
113
121
|
self.logger.info(f"Shutting down service: {self.service_name}")
|
|
114
|
-
|
|
122
|
+
|
|
115
123
|
try:
|
|
116
124
|
# Signal shutdown to background tasks
|
|
117
125
|
self._shutdown_event.set()
|
|
118
|
-
|
|
126
|
+
|
|
119
127
|
# Cancel background tasks
|
|
120
128
|
await self._cancel_background_tasks()
|
|
121
|
-
|
|
129
|
+
|
|
122
130
|
# Service-specific shutdown
|
|
123
131
|
await self._do_shutdown()
|
|
124
|
-
|
|
132
|
+
|
|
125
133
|
self._state = AsyncServiceState.STOPPED
|
|
126
134
|
self.logger.info(f"Service {self.service_name} shut down successfully")
|
|
127
|
-
|
|
135
|
+
|
|
128
136
|
except Exception as e:
|
|
129
137
|
self._state = AsyncServiceState.ERROR
|
|
130
138
|
self._last_error = e
|
|
131
|
-
self.logger.error(
|
|
132
|
-
|
|
139
|
+
self.logger.error(
|
|
140
|
+
f"Service {self.service_name} shutdown error: {e}", exc_info=True
|
|
141
|
+
)
|
|
142
|
+
|
|
133
143
|
async def restart(self) -> bool:
|
|
134
144
|
"""Restart the service."""
|
|
135
145
|
self.logger.info(f"Restarting service: {self.service_name}")
|
|
136
146
|
await self.shutdown()
|
|
137
|
-
|
|
147
|
+
|
|
138
148
|
# Reset state for restart
|
|
139
149
|
self._state = AsyncServiceState.UNINITIALIZED
|
|
140
150
|
self._shutdown_event.clear()
|
|
141
151
|
self._last_error = None
|
|
142
|
-
|
|
152
|
+
|
|
143
153
|
return await self.initialize()
|
|
144
|
-
|
|
145
|
-
def create_background_task(self, coro, name: str = None) -> asyncio.Task:
|
|
154
|
+
|
|
155
|
+
def create_background_task(self, coro, name: Optional[str] = None) -> asyncio.Task:
|
|
146
156
|
"""
|
|
147
157
|
Create and track a background task.
|
|
148
|
-
|
|
158
|
+
|
|
149
159
|
Args:
|
|
150
160
|
coro: Coroutine to run
|
|
151
161
|
name: Optional task name
|
|
152
|
-
|
|
162
|
+
|
|
153
163
|
Returns:
|
|
154
164
|
Created task
|
|
155
165
|
"""
|
|
156
166
|
task = asyncio.create_task(coro, name=name)
|
|
157
167
|
self._background_tasks.add(task)
|
|
158
|
-
|
|
168
|
+
|
|
159
169
|
# Remove task from set when done
|
|
160
170
|
task.add_done_callback(self._background_tasks.discard)
|
|
161
|
-
|
|
171
|
+
|
|
162
172
|
return task
|
|
163
|
-
|
|
173
|
+
|
|
164
174
|
async def _cancel_background_tasks(self) -> None:
|
|
165
175
|
"""Cancel all background tasks."""
|
|
166
176
|
if not self._background_tasks:
|
|
167
177
|
return
|
|
168
|
-
|
|
178
|
+
|
|
169
179
|
self.logger.debug(f"Cancelling {len(self._background_tasks)} background tasks")
|
|
170
|
-
|
|
180
|
+
|
|
171
181
|
# Cancel all tasks
|
|
172
182
|
for task in self._background_tasks:
|
|
173
183
|
if not task.done():
|
|
174
184
|
task.cancel()
|
|
175
|
-
|
|
185
|
+
|
|
176
186
|
# Wait for cancellation with timeout
|
|
177
187
|
try:
|
|
178
188
|
await asyncio.wait_for(
|
|
179
189
|
asyncio.gather(*self._background_tasks, return_exceptions=True),
|
|
180
|
-
timeout=5.0
|
|
190
|
+
timeout=5.0,
|
|
181
191
|
)
|
|
182
192
|
except asyncio.TimeoutError:
|
|
183
193
|
self.logger.warning("Some background tasks did not cancel within timeout")
|
|
184
|
-
|
|
194
|
+
|
|
185
195
|
self._background_tasks.clear()
|
|
186
|
-
|
|
196
|
+
|
|
187
197
|
async def health_check(self) -> Dict[str, Any]:
|
|
188
198
|
"""
|
|
189
199
|
Perform health check.
|
|
190
|
-
|
|
200
|
+
|
|
191
201
|
Returns:
|
|
192
202
|
Health status dictionary
|
|
193
203
|
"""
|
|
@@ -197,23 +207,21 @@ class AsyncServiceBase(LoggerMixin, ABC):
|
|
|
197
207
|
"healthy": self.is_healthy,
|
|
198
208
|
"error_count": self._error_count,
|
|
199
209
|
"last_error": str(self._last_error) if self._last_error else None,
|
|
200
|
-
"background_tasks": len(self._background_tasks)
|
|
210
|
+
"background_tasks": len(self._background_tasks),
|
|
201
211
|
}
|
|
202
|
-
|
|
212
|
+
|
|
203
213
|
@abstractmethod
|
|
204
214
|
async def _do_initialize(self) -> bool:
|
|
205
215
|
"""
|
|
206
216
|
Service-specific initialization logic.
|
|
207
|
-
|
|
217
|
+
|
|
208
218
|
Returns:
|
|
209
219
|
True if initialization successful
|
|
210
220
|
"""
|
|
211
|
-
|
|
212
|
-
|
|
221
|
+
|
|
213
222
|
async def _do_shutdown(self) -> None:
|
|
214
223
|
"""Service-specific shutdown logic."""
|
|
215
|
-
|
|
216
|
-
|
|
224
|
+
|
|
217
225
|
def __repr__(self) -> str:
|
|
218
226
|
"""String representation."""
|
|
219
227
|
return f"{self.__class__.__name__}(name={self.service_name}, state={self._state.value})"
|