claude-mpm 4.1.1__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 +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 +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.1.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.1.dist-info/RECORD +0 -494
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.1.dist-info → claude_mpm-4.1.2.dist-info}/top_level.txt +0 -0
|
@@ -6,6 +6,7 @@ The central event bus that manages event flow from producers to consumers.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import asyncio
|
|
9
|
+
import contextlib
|
|
9
10
|
import re
|
|
10
11
|
import time
|
|
11
12
|
import uuid
|
|
@@ -22,6 +23,7 @@ from .interfaces import ConsumerPriority, IEventBus, IEventConsumer
|
|
|
22
23
|
|
|
23
24
|
class EventPriority(Enum):
|
|
24
25
|
"""Priority levels for events."""
|
|
26
|
+
|
|
25
27
|
CRITICAL = 1
|
|
26
28
|
HIGH = 2
|
|
27
29
|
NORMAL = 3
|
|
@@ -31,6 +33,7 @@ class EventPriority(Enum):
|
|
|
31
33
|
@dataclass
|
|
32
34
|
class EventMetadata:
|
|
33
35
|
"""Metadata associated with an event."""
|
|
36
|
+
|
|
34
37
|
retry_count: int = 0
|
|
35
38
|
max_retries: int = 3
|
|
36
39
|
published_at: Optional[datetime] = None
|
|
@@ -44,19 +47,20 @@ class EventMetadata:
|
|
|
44
47
|
class Event:
|
|
45
48
|
"""
|
|
46
49
|
Standard event format for the event bus.
|
|
47
|
-
|
|
50
|
+
|
|
48
51
|
All events flowing through the system use this format.
|
|
49
52
|
"""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
|
|
54
|
+
id: str # Unique event ID
|
|
55
|
+
topic: str # Event topic (e.g., "hook.response")
|
|
56
|
+
type: str # Event type (e.g., "AssistantResponse")
|
|
57
|
+
timestamp: datetime # When event was created
|
|
58
|
+
source: str # Who created the event
|
|
59
|
+
data: Dict[str, Any] # Event payload
|
|
56
60
|
metadata: Optional[EventMetadata] = None # Event metadata
|
|
57
|
-
correlation_id: Optional[str] = None
|
|
61
|
+
correlation_id: Optional[str] = None # For tracking related events
|
|
58
62
|
priority: EventPriority = EventPriority.NORMAL
|
|
59
|
-
|
|
63
|
+
|
|
60
64
|
def __post_init__(self):
|
|
61
65
|
if self.metadata is None:
|
|
62
66
|
self.metadata = EventMetadata()
|
|
@@ -64,35 +68,35 @@ class Event:
|
|
|
64
68
|
self.id = str(uuid.uuid4())
|
|
65
69
|
if isinstance(self.timestamp, str):
|
|
66
70
|
self.timestamp = datetime.fromisoformat(self.timestamp)
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
def matches_topic(self, pattern: str) -> bool:
|
|
69
73
|
"""
|
|
70
74
|
Check if event matches a topic pattern.
|
|
71
|
-
|
|
75
|
+
|
|
72
76
|
Supports wildcards:
|
|
73
77
|
- * matches any single segment
|
|
74
78
|
- ** matches any number of segments
|
|
75
|
-
|
|
79
|
+
|
|
76
80
|
Examples:
|
|
77
81
|
- "hook.*" matches "hook.response" but not "hook.tool.usage"
|
|
78
82
|
- "hook.**" matches both "hook.response" and "hook.tool.usage"
|
|
79
83
|
"""
|
|
80
|
-
if pattern
|
|
84
|
+
if pattern in {"**", "*"}:
|
|
81
85
|
return True
|
|
82
|
-
|
|
86
|
+
|
|
83
87
|
# Convert wildcard pattern to regex
|
|
84
88
|
regex_pattern = pattern.replace(".", r"\.")
|
|
85
89
|
regex_pattern = regex_pattern.replace("**", ".*")
|
|
86
90
|
regex_pattern = regex_pattern.replace("*", "[^.]+")
|
|
87
91
|
regex_pattern = f"^{regex_pattern}$"
|
|
88
|
-
|
|
92
|
+
|
|
89
93
|
return bool(re.match(regex_pattern, self.topic))
|
|
90
94
|
|
|
91
95
|
|
|
92
96
|
class EventBus(IEventBus):
|
|
93
97
|
"""
|
|
94
98
|
Central event bus implementation.
|
|
95
|
-
|
|
99
|
+
|
|
96
100
|
Features:
|
|
97
101
|
- Async event processing
|
|
98
102
|
- Topic-based routing
|
|
@@ -101,7 +105,7 @@ class EventBus(IEventBus):
|
|
|
101
105
|
- Metrics tracking
|
|
102
106
|
- Optional persistence
|
|
103
107
|
"""
|
|
104
|
-
|
|
108
|
+
|
|
105
109
|
def __init__(
|
|
106
110
|
self,
|
|
107
111
|
max_queue_size: int = 10000,
|
|
@@ -112,7 +116,7 @@ class EventBus(IEventBus):
|
|
|
112
116
|
):
|
|
113
117
|
"""
|
|
114
118
|
Initialize the event bus.
|
|
115
|
-
|
|
119
|
+
|
|
116
120
|
Args:
|
|
117
121
|
max_queue_size: Maximum events in queue
|
|
118
122
|
process_interval: How often to process events (seconds)
|
|
@@ -121,29 +125,28 @@ class EventBus(IEventBus):
|
|
|
121
125
|
enable_persistence: Persist events to disk
|
|
122
126
|
"""
|
|
123
127
|
self.logger = get_logger("EventBus")
|
|
124
|
-
|
|
128
|
+
|
|
125
129
|
# Configuration
|
|
126
130
|
self.max_queue_size = max_queue_size
|
|
127
131
|
self.process_interval = process_interval
|
|
128
132
|
self.batch_timeout = batch_timeout
|
|
129
133
|
self.enable_metrics = enable_metrics
|
|
130
134
|
self.enable_persistence = enable_persistence
|
|
131
|
-
|
|
135
|
+
|
|
132
136
|
# State
|
|
133
137
|
self._running = False
|
|
134
138
|
self._processing_task: Optional[asyncio.Task] = None
|
|
135
|
-
|
|
139
|
+
|
|
136
140
|
# Event queue (priority-based)
|
|
137
141
|
self._event_queues: Dict[EventPriority, Deque[Event]] = {
|
|
138
|
-
priority: deque(maxlen=max_queue_size // 4)
|
|
139
|
-
for priority in EventPriority
|
|
142
|
+
priority: deque(maxlen=max_queue_size // 4) for priority in EventPriority
|
|
140
143
|
}
|
|
141
|
-
|
|
144
|
+
|
|
142
145
|
# Consumers
|
|
143
146
|
self._consumers: Dict[str, IEventConsumer] = {}
|
|
144
147
|
self._consumer_topics: Dict[str, List[str]] = {}
|
|
145
148
|
self._topic_consumers: Dict[str, Set[str]] = defaultdict(set)
|
|
146
|
-
|
|
149
|
+
|
|
147
150
|
# Metrics
|
|
148
151
|
self._metrics = {
|
|
149
152
|
"events_published": 0,
|
|
@@ -155,98 +158,102 @@ class EventBus(IEventBus):
|
|
|
155
158
|
"processing_time_ms": 0,
|
|
156
159
|
"last_event_time": None,
|
|
157
160
|
}
|
|
158
|
-
|
|
161
|
+
|
|
159
162
|
# Dead letter queue for failed events
|
|
160
163
|
self._dead_letter_queue: Deque[Event] = deque(maxlen=1000)
|
|
161
|
-
|
|
164
|
+
|
|
162
165
|
async def start(self) -> None:
|
|
163
166
|
"""Start the event bus."""
|
|
164
167
|
if self._running:
|
|
165
168
|
self.logger.warning("Event bus already running")
|
|
166
169
|
return
|
|
167
|
-
|
|
170
|
+
|
|
168
171
|
self.logger.info("Starting event bus")
|
|
169
172
|
self._running = True
|
|
170
|
-
|
|
173
|
+
|
|
171
174
|
# Start processing task
|
|
172
175
|
self._processing_task = asyncio.create_task(self._process_events())
|
|
173
|
-
|
|
176
|
+
|
|
174
177
|
self.logger.info("Event bus started")
|
|
175
|
-
|
|
178
|
+
|
|
176
179
|
async def stop(self) -> None:
|
|
177
180
|
"""Stop the event bus gracefully."""
|
|
178
181
|
if not self._running:
|
|
179
182
|
return
|
|
180
|
-
|
|
183
|
+
|
|
181
184
|
self.logger.info("Stopping event bus")
|
|
182
185
|
self._running = False
|
|
183
|
-
|
|
186
|
+
|
|
184
187
|
# Wait for processing to complete
|
|
185
188
|
if self._processing_task:
|
|
186
189
|
self._processing_task.cancel()
|
|
187
|
-
|
|
190
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
188
191
|
await self._processing_task
|
|
189
|
-
|
|
190
|
-
pass
|
|
191
|
-
|
|
192
|
+
|
|
192
193
|
# Process remaining events
|
|
193
194
|
await self._flush_events()
|
|
194
|
-
|
|
195
|
+
|
|
195
196
|
# Shutdown consumers
|
|
196
197
|
for consumer in self._consumers.values():
|
|
197
198
|
try:
|
|
198
199
|
await consumer.shutdown()
|
|
199
200
|
except Exception as e:
|
|
200
|
-
self.logger.error(
|
|
201
|
-
|
|
201
|
+
self.logger.error(
|
|
202
|
+
f"Error shutting down consumer {consumer.config.name}: {e}"
|
|
203
|
+
)
|
|
204
|
+
|
|
202
205
|
self.logger.info("Event bus stopped")
|
|
203
|
-
|
|
206
|
+
|
|
204
207
|
async def publish(self, event: Event) -> bool:
|
|
205
208
|
"""
|
|
206
209
|
Publish an event to the bus.
|
|
207
|
-
|
|
210
|
+
|
|
208
211
|
Events are queued based on priority and processed asynchronously.
|
|
209
212
|
"""
|
|
210
213
|
if not self._running:
|
|
211
214
|
self.logger.warning("Cannot publish event - bus not running")
|
|
212
215
|
return False
|
|
213
|
-
|
|
216
|
+
|
|
214
217
|
# Check queue size
|
|
215
218
|
total_size = sum(len(q) for q in self._event_queues.values())
|
|
216
219
|
if total_size >= self.max_queue_size:
|
|
217
|
-
self.logger.error(
|
|
220
|
+
self.logger.error(
|
|
221
|
+
f"Event queue full ({total_size}/{self.max_queue_size}), dropping event"
|
|
222
|
+
)
|
|
218
223
|
self._metrics["events_dropped"] += 1
|
|
219
224
|
return False
|
|
220
|
-
|
|
225
|
+
|
|
221
226
|
# Add metadata
|
|
222
227
|
if event.metadata:
|
|
223
228
|
event.metadata.published_at = datetime.now()
|
|
224
|
-
|
|
229
|
+
|
|
225
230
|
# Queue event
|
|
226
231
|
self._event_queues[event.priority].append(event)
|
|
227
232
|
self._metrics["events_published"] += 1
|
|
228
233
|
self._metrics["queue_size"] = total_size + 1
|
|
229
|
-
|
|
230
|
-
self.logger.debug(
|
|
234
|
+
|
|
235
|
+
self.logger.debug(
|
|
236
|
+
f"Published event: {event.topic}/{event.type} (priority={event.priority.name})"
|
|
237
|
+
)
|
|
231
238
|
return True
|
|
232
|
-
|
|
239
|
+
|
|
233
240
|
async def subscribe(self, consumer: IEventConsumer) -> bool:
|
|
234
241
|
"""Subscribe a consumer to the bus."""
|
|
235
242
|
config = consumer.config
|
|
236
|
-
|
|
243
|
+
|
|
237
244
|
if config.name in self._consumers:
|
|
238
245
|
self.logger.warning(f"Consumer {config.name} already subscribed")
|
|
239
246
|
return False
|
|
240
|
-
|
|
247
|
+
|
|
241
248
|
try:
|
|
242
249
|
# Initialize consumer
|
|
243
250
|
if not await consumer.initialize():
|
|
244
251
|
self.logger.error(f"Failed to initialize consumer {config.name}")
|
|
245
252
|
return False
|
|
246
|
-
|
|
253
|
+
|
|
247
254
|
# Register consumer
|
|
248
255
|
self._consumers[config.name] = consumer
|
|
249
|
-
|
|
256
|
+
|
|
250
257
|
# Register topics
|
|
251
258
|
if config.topics:
|
|
252
259
|
self._consumer_topics[config.name] = config.topics
|
|
@@ -256,53 +263,53 @@ class EventBus(IEventBus):
|
|
|
256
263
|
# Consumer receives all events
|
|
257
264
|
self._consumer_topics[config.name] = ["**"]
|
|
258
265
|
self._topic_consumers["**"].add(config.name)
|
|
259
|
-
|
|
266
|
+
|
|
260
267
|
self._metrics["consumers_active"] = len(self._consumers)
|
|
261
|
-
|
|
268
|
+
|
|
262
269
|
self.logger.info(
|
|
263
270
|
f"Subscribed consumer {config.name} to topics: "
|
|
264
271
|
f"{self._consumer_topics[config.name]}"
|
|
265
272
|
)
|
|
266
273
|
return True
|
|
267
|
-
|
|
274
|
+
|
|
268
275
|
except Exception as e:
|
|
269
276
|
self.logger.error(f"Error subscribing consumer {config.name}: {e}")
|
|
270
277
|
return False
|
|
271
|
-
|
|
278
|
+
|
|
272
279
|
async def unsubscribe(self, consumer_name: str) -> bool:
|
|
273
280
|
"""Unsubscribe a consumer from the bus."""
|
|
274
281
|
if consumer_name not in self._consumers:
|
|
275
282
|
self.logger.warning(f"Consumer {consumer_name} not found")
|
|
276
283
|
return False
|
|
277
|
-
|
|
284
|
+
|
|
278
285
|
try:
|
|
279
286
|
consumer = self._consumers[consumer_name]
|
|
280
|
-
|
|
287
|
+
|
|
281
288
|
# Shutdown consumer
|
|
282
289
|
await consumer.shutdown()
|
|
283
|
-
|
|
290
|
+
|
|
284
291
|
# Remove from registries
|
|
285
292
|
del self._consumers[consumer_name]
|
|
286
|
-
|
|
293
|
+
|
|
287
294
|
# Remove topic subscriptions
|
|
288
295
|
if consumer_name in self._consumer_topics:
|
|
289
296
|
for topic in self._consumer_topics[consumer_name]:
|
|
290
297
|
self._topic_consumers[topic].discard(consumer_name)
|
|
291
298
|
del self._consumer_topics[consumer_name]
|
|
292
|
-
|
|
299
|
+
|
|
293
300
|
self._metrics["consumers_active"] = len(self._consumers)
|
|
294
|
-
|
|
301
|
+
|
|
295
302
|
self.logger.info(f"Unsubscribed consumer {consumer_name}")
|
|
296
303
|
return True
|
|
297
|
-
|
|
304
|
+
|
|
298
305
|
except Exception as e:
|
|
299
306
|
self.logger.error(f"Error unsubscribing consumer {consumer_name}: {e}")
|
|
300
307
|
return False
|
|
301
|
-
|
|
308
|
+
|
|
302
309
|
def get_consumers(self) -> List[IEventConsumer]:
|
|
303
310
|
"""Get list of active consumers."""
|
|
304
311
|
return list(self._consumers.values())
|
|
305
|
-
|
|
312
|
+
|
|
306
313
|
def get_metrics(self) -> Dict[str, Any]:
|
|
307
314
|
"""Get event bus metrics."""
|
|
308
315
|
return {
|
|
@@ -311,18 +318,18 @@ class EventBus(IEventBus):
|
|
|
311
318
|
"consumers": {
|
|
312
319
|
name: consumer.get_metrics()
|
|
313
320
|
for name, consumer in self._consumers.items()
|
|
314
|
-
}
|
|
321
|
+
},
|
|
315
322
|
}
|
|
316
|
-
|
|
323
|
+
|
|
317
324
|
@property
|
|
318
325
|
def is_running(self) -> bool:
|
|
319
326
|
"""Check if event bus is running."""
|
|
320
327
|
return self._running
|
|
321
|
-
|
|
328
|
+
|
|
322
329
|
async def _process_events(self) -> None:
|
|
323
330
|
"""
|
|
324
331
|
Main event processing loop.
|
|
325
|
-
|
|
332
|
+
|
|
326
333
|
Continuously processes events from the queue and routes them
|
|
327
334
|
to appropriate consumers.
|
|
328
335
|
"""
|
|
@@ -330,73 +337,77 @@ class EventBus(IEventBus):
|
|
|
330
337
|
try:
|
|
331
338
|
# Process events by priority
|
|
332
339
|
events_processed = 0
|
|
333
|
-
|
|
340
|
+
|
|
334
341
|
for priority in EventPriority:
|
|
335
342
|
queue = self._event_queues[priority]
|
|
336
|
-
|
|
343
|
+
|
|
337
344
|
# Process up to batch_size events
|
|
338
345
|
batch = []
|
|
339
346
|
while queue and len(batch) < 10:
|
|
340
347
|
batch.append(queue.popleft())
|
|
341
|
-
|
|
348
|
+
|
|
342
349
|
if batch:
|
|
343
350
|
await self._route_events(batch)
|
|
344
351
|
events_processed += len(batch)
|
|
345
|
-
|
|
352
|
+
|
|
346
353
|
# Update metrics
|
|
347
354
|
if events_processed > 0:
|
|
348
355
|
self._metrics["events_processed"] += events_processed
|
|
349
356
|
self._metrics["last_event_time"] = datetime.now()
|
|
350
|
-
self._metrics["queue_size"] = sum(
|
|
351
|
-
|
|
357
|
+
self._metrics["queue_size"] = sum(
|
|
358
|
+
len(q) for q in self._event_queues.values()
|
|
359
|
+
)
|
|
360
|
+
|
|
352
361
|
# Sleep if no events
|
|
353
362
|
if events_processed == 0:
|
|
354
363
|
await asyncio.sleep(self.process_interval)
|
|
355
|
-
|
|
364
|
+
|
|
356
365
|
except Exception as e:
|
|
357
366
|
self.logger.error(f"Error in event processing loop: {e}")
|
|
358
367
|
await asyncio.sleep(1) # Back off on error
|
|
359
|
-
|
|
368
|
+
|
|
360
369
|
async def _route_events(self, events: List[Event]) -> None:
|
|
361
370
|
"""
|
|
362
371
|
Route events to appropriate consumers.
|
|
363
|
-
|
|
372
|
+
|
|
364
373
|
Events are routed based on topic subscriptions.
|
|
365
374
|
Consumers are called in priority order.
|
|
366
375
|
"""
|
|
367
376
|
for event in events:
|
|
368
377
|
# Find matching consumers
|
|
369
378
|
matching_consumers = set()
|
|
370
|
-
|
|
379
|
+
|
|
371
380
|
# Check exact topic matches
|
|
372
381
|
if event.topic in self._topic_consumers:
|
|
373
382
|
matching_consumers.update(self._topic_consumers[event.topic])
|
|
374
|
-
|
|
383
|
+
|
|
375
384
|
# Check wildcard subscriptions
|
|
376
385
|
for pattern, consumers in self._topic_consumers.items():
|
|
377
386
|
if "*" in pattern and event.matches_topic(pattern):
|
|
378
387
|
matching_consumers.update(consumers)
|
|
379
|
-
|
|
388
|
+
|
|
380
389
|
# Check consumers with no specific topics (receive all)
|
|
381
390
|
if "**" in self._topic_consumers:
|
|
382
391
|
matching_consumers.update(self._topic_consumers["**"])
|
|
383
|
-
|
|
392
|
+
|
|
384
393
|
# Sort consumers by priority
|
|
385
394
|
consumers_by_priority = defaultdict(list)
|
|
386
395
|
for consumer_name in matching_consumers:
|
|
387
396
|
if consumer_name in self._consumers:
|
|
388
397
|
consumer = self._consumers[consumer_name]
|
|
389
398
|
consumers_by_priority[consumer.config.priority].append(consumer)
|
|
390
|
-
|
|
399
|
+
|
|
391
400
|
# Process event with each consumer
|
|
392
401
|
for priority in ConsumerPriority:
|
|
393
402
|
for consumer in consumers_by_priority[priority]:
|
|
394
403
|
await self._deliver_to_consumer(event, consumer)
|
|
395
|
-
|
|
396
|
-
async def _deliver_to_consumer(
|
|
404
|
+
|
|
405
|
+
async def _deliver_to_consumer(
|
|
406
|
+
self, event: Event, consumer: IEventConsumer
|
|
407
|
+
) -> None:
|
|
397
408
|
"""
|
|
398
409
|
Deliver an event to a specific consumer.
|
|
399
|
-
|
|
410
|
+
|
|
400
411
|
Handles errors gracefully without affecting other consumers.
|
|
401
412
|
"""
|
|
402
413
|
try:
|
|
@@ -404,16 +415,16 @@ class EventBus(IEventBus):
|
|
|
404
415
|
if consumer.config.filter_func:
|
|
405
416
|
if not consumer.config.filter_func(event):
|
|
406
417
|
return
|
|
407
|
-
|
|
418
|
+
|
|
408
419
|
# Apply transformation if configured
|
|
409
420
|
if consumer.config.transform_func:
|
|
410
421
|
event = consumer.config.transform_func(event)
|
|
411
|
-
|
|
422
|
+
|
|
412
423
|
# Process event
|
|
413
424
|
start_time = time.time()
|
|
414
425
|
success = await consumer.consume(event)
|
|
415
426
|
elapsed_ms = (time.time() - start_time) * 1000
|
|
416
|
-
|
|
427
|
+
|
|
417
428
|
# Update metrics
|
|
418
429
|
if success:
|
|
419
430
|
event.metadata.consumers_processed.add(consumer.config.name)
|
|
@@ -426,12 +437,12 @@ class EventBus(IEventBus):
|
|
|
426
437
|
self.logger.warning(
|
|
427
438
|
f"Consumer {consumer.config.name} failed to process event {event.id}"
|
|
428
439
|
)
|
|
429
|
-
|
|
440
|
+
|
|
430
441
|
# Add to dead letter queue if all retries exhausted
|
|
431
442
|
if event.metadata.retry_count >= event.metadata.max_retries:
|
|
432
443
|
self._dead_letter_queue.append(event)
|
|
433
444
|
self._metrics["events_failed"] += 1
|
|
434
|
-
|
|
445
|
+
|
|
435
446
|
except Exception as e:
|
|
436
447
|
self.logger.error(
|
|
437
448
|
f"Error delivering event {event.id} to consumer "
|
|
@@ -439,7 +450,7 @@ class EventBus(IEventBus):
|
|
|
439
450
|
)
|
|
440
451
|
event.metadata.consumers_failed.add(consumer.config.name)
|
|
441
452
|
event.metadata.error_messages.append(str(e))
|
|
442
|
-
|
|
453
|
+
|
|
443
454
|
# Use custom error handler if provided
|
|
444
455
|
if consumer.config.error_handler:
|
|
445
456
|
try:
|
|
@@ -449,22 +460,22 @@ class EventBus(IEventBus):
|
|
|
449
460
|
f"Error in custom error handler for {consumer.config.name}: "
|
|
450
461
|
f"{handler_error}"
|
|
451
462
|
)
|
|
452
|
-
|
|
463
|
+
|
|
453
464
|
async def _flush_events(self) -> None:
|
|
454
465
|
"""Process all remaining events in the queue."""
|
|
455
466
|
total_events = sum(len(q) for q in self._event_queues.values())
|
|
456
|
-
|
|
467
|
+
|
|
457
468
|
if total_events > 0:
|
|
458
469
|
self.logger.info(f"Flushing {total_events} remaining events")
|
|
459
|
-
|
|
470
|
+
|
|
460
471
|
for priority in EventPriority:
|
|
461
472
|
queue = self._event_queues[priority]
|
|
462
473
|
while queue:
|
|
463
474
|
batch = []
|
|
464
475
|
for _ in range(min(10, len(queue))):
|
|
465
476
|
batch.append(queue.popleft())
|
|
466
|
-
|
|
477
|
+
|
|
467
478
|
if batch:
|
|
468
479
|
await self._route_events(batch)
|
|
469
|
-
|
|
470
|
-
self.logger.info("Event flush complete")
|
|
480
|
+
|
|
481
|
+
self.logger.info("Event flush complete")
|