claude-mpm 3.4.10__py3-none-any.whl → 5.4.55__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 -0
- claude_mpm/VERSION +1 -0
- claude_mpm/__init__.py +50 -12
- claude_mpm/__main__.py +7 -2
- claude_mpm/agents/BASE_AGENT.md +164 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +290 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +2002 -0
- claude_mpm/agents/MEMORY.md +72 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +1402 -0
- claude_mpm/agents/WORKFLOW.md +111 -0
- claude_mpm/agents/__init__.py +92 -80
- claude_mpm/agents/agent-template.yaml +83 -0
- claude_mpm/agents/agent_loader.py +560 -745
- claude_mpm/agents/agent_loader_integration.py +53 -55
- claude_mpm/agents/agents_metadata.py +186 -27
- claude_mpm/agents/async_agent_loader.py +436 -0
- claude_mpm/agents/base_agent.json +8 -4
- claude_mpm/agents/frontmatter_validator.py +754 -0
- claude_mpm/agents/system_agent_config.py +222 -155
- claude_mpm/agents/templates/README.md +465 -0
- claude_mpm/agents/templates/__init__.py +17 -13
- claude_mpm/agents/templates/circuit-breakers.md +1391 -0
- claude_mpm/agents/templates/context-management-examples.md +544 -0
- claude_mpm/agents/templates/git-file-tracking.md +584 -0
- claude_mpm/agents/templates/pm-examples.md +474 -0
- claude_mpm/agents/templates/pm-red-flags.md +310 -0
- claude_mpm/agents/templates/pr-workflow-examples.md +427 -0
- claude_mpm/agents/templates/research-gate-examples.md +669 -0
- claude_mpm/agents/templates/response-format.md +583 -0
- claude_mpm/agents/templates/structured-questions-examples.md +615 -0
- claude_mpm/agents/templates/ticket-completeness-examples.md +139 -0
- claude_mpm/agents/templates/ticketing-examples.md +277 -0
- claude_mpm/agents/templates/validation-templates.md +312 -0
- claude_mpm/cli/__init__.py +90 -128
- claude_mpm/cli/__main__.py +33 -0
- claude_mpm/cli/chrome_devtools_installer.py +175 -0
- claude_mpm/cli/commands/__init__.py +36 -12
- claude_mpm/cli/commands/agent_manager.py +1403 -0
- claude_mpm/cli/commands/agent_source.py +774 -0
- claude_mpm/cli/commands/agent_state_manager.py +335 -0
- claude_mpm/cli/commands/agents.py +2503 -168
- claude_mpm/cli/commands/agents_cleanup.py +210 -0
- claude_mpm/cli/commands/agents_discover.py +338 -0
- claude_mpm/cli/commands/aggregate.py +540 -0
- claude_mpm/cli/commands/analyze.py +553 -0
- claude_mpm/cli/commands/analyze_code.py +528 -0
- claude_mpm/cli/commands/auto_configure.py +1053 -0
- claude_mpm/cli/commands/cleanup.py +588 -0
- claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
- claude_mpm/cli/commands/config.py +586 -0
- claude_mpm/cli/commands/configure.py +2654 -0
- claude_mpm/cli/commands/configure_agent_display.py +282 -0
- claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
- claude_mpm/cli/commands/configure_hook_manager.py +225 -0
- claude_mpm/cli/commands/configure_models.py +18 -0
- claude_mpm/cli/commands/configure_navigation.py +184 -0
- claude_mpm/cli/commands/configure_paths.py +104 -0
- claude_mpm/cli/commands/configure_persistence.py +254 -0
- claude_mpm/cli/commands/configure_startup_manager.py +646 -0
- claude_mpm/cli/commands/configure_template_editor.py +497 -0
- claude_mpm/cli/commands/configure_validators.py +73 -0
- claude_mpm/cli/commands/dashboard.py +286 -0
- claude_mpm/cli/commands/debug.py +1386 -0
- claude_mpm/cli/commands/doctor.py +243 -0
- claude_mpm/cli/commands/hook_errors.py +277 -0
- claude_mpm/cli/commands/info.py +195 -74
- claude_mpm/cli/commands/local_deploy.py +534 -0
- claude_mpm/cli/commands/mcp.py +205 -0
- claude_mpm/cli/commands/mcp_command_router.py +161 -0
- claude_mpm/cli/commands/mcp_config.py +154 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_external_commands.py +249 -0
- claude_mpm/cli/commands/mcp_install_commands.py +346 -0
- claude_mpm/cli/commands/mcp_pipx_config.py +208 -0
- claude_mpm/cli/commands/mcp_server_commands.py +155 -0
- claude_mpm/cli/commands/mcp_setup_external.py +868 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +585 -846
- claude_mpm/cli/commands/monitor.py +228 -310
- claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
- claude_mpm/cli/commands/mpm_init/core.py +759 -0
- claude_mpm/cli/commands/mpm_init/display.py +341 -0
- claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
- claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
- claude_mpm/cli/commands/mpm_init/modes.py +397 -0
- claude_mpm/cli/commands/mpm_init/prompts.py +722 -0
- claude_mpm/cli/commands/mpm_init_cli.py +396 -0
- claude_mpm/cli/commands/mpm_init_handler.py +195 -0
- claude_mpm/cli/commands/postmortem.py +401 -0
- claude_mpm/cli/commands/profile.py +276 -0
- claude_mpm/cli/commands/run.py +910 -488
- claude_mpm/cli/commands/search.py +458 -0
- claude_mpm/cli/commands/skill_source.py +694 -0
- claude_mpm/cli/commands/skills.py +1246 -0
- claude_mpm/cli/commands/summarize.py +413 -0
- claude_mpm/cli/commands/tickets.py +536 -53
- claude_mpm/cli/commands/uninstall.py +176 -0
- claude_mpm/cli/commands/upgrade.py +152 -0
- claude_mpm/cli/commands/verify.py +119 -0
- claude_mpm/cli/executor.py +297 -0
- claude_mpm/cli/helpers.py +105 -0
- claude_mpm/cli/interactive/__init__.py +21 -0
- claude_mpm/cli/interactive/agent_wizard.py +1947 -0
- claude_mpm/cli/interactive/skills_wizard.py +491 -0
- claude_mpm/cli/parser.py +87 -563
- claude_mpm/cli/parsers/__init__.py +35 -0
- claude_mpm/cli/parsers/agent_manager_parser.py +393 -0
- claude_mpm/cli/parsers/agent_source_parser.py +171 -0
- claude_mpm/cli/parsers/agents_parser.py +575 -0
- claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
- claude_mpm/cli/parsers/analyze_parser.py +135 -0
- claude_mpm/cli/parsers/auto_configure_parser.py +120 -0
- claude_mpm/cli/parsers/base_parser.py +644 -0
- claude_mpm/cli/parsers/config_parser.py +208 -0
- claude_mpm/cli/parsers/configure_parser.py +138 -0
- claude_mpm/cli/parsers/dashboard_parser.py +113 -0
- claude_mpm/cli/parsers/debug_parser.py +319 -0
- claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
- claude_mpm/cli/parsers/mcp_parser.py +195 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +142 -0
- claude_mpm/cli/parsers/mpm_init_parser.py +311 -0
- claude_mpm/cli/parsers/profile_parser.py +147 -0
- claude_mpm/cli/parsers/run_parser.py +157 -0
- claude_mpm/cli/parsers/search_parser.py +245 -0
- claude_mpm/cli/parsers/skill_source_parser.py +169 -0
- claude_mpm/cli/parsers/skills_parser.py +277 -0
- claude_mpm/cli/parsers/source_parser.py +138 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/shared/__init__.py +40 -0
- claude_mpm/cli/shared/argument_patterns.py +205 -0
- claude_mpm/cli/shared/base_command.py +242 -0
- claude_mpm/cli/shared/error_handling.py +242 -0
- claude_mpm/cli/shared/output_formatters.py +241 -0
- claude_mpm/cli/startup.py +1743 -0
- claude_mpm/cli/startup_display.py +480 -0
- claude_mpm/cli/startup_logging.py +839 -0
- claude_mpm/cli/utils.py +136 -47
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +42 -64
- claude_mpm/commands/__init__.py +14 -0
- claude_mpm/commands/mpm-config.md +28 -0
- claude_mpm/commands/mpm-doctor.md +20 -0
- claude_mpm/commands/mpm-help.md +20 -0
- claude_mpm/commands/mpm-init.md +120 -0
- claude_mpm/commands/mpm-monitor.md +31 -0
- claude_mpm/commands/mpm-organize.md +120 -0
- claude_mpm/commands/mpm-postmortem.md +21 -0
- claude_mpm/commands/mpm-session-resume.md +30 -0
- claude_mpm/commands/mpm-status.md +20 -0
- claude_mpm/commands/mpm-ticket-view.md +109 -0
- claude_mpm/commands/mpm-version.md +20 -0
- claude_mpm/commands/mpm.md +31 -0
- claude_mpm/config/__init__.py +42 -2
- claude_mpm/config/agent_config.py +402 -0
- claude_mpm/config/agent_presets.py +488 -0
- claude_mpm/config/agent_sources.py +352 -0
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/model_config.py +428 -0
- claude_mpm/config/paths.py +258 -0
- claude_mpm/config/skill_presets.py +392 -0
- claude_mpm/config/skill_sources.py +590 -0
- claude_mpm/config/socketio_config.py +125 -83
- claude_mpm/constants.py +132 -22
- claude_mpm/core/__init__.py +62 -36
- claude_mpm/core/agent_name_normalizer.py +71 -73
- claude_mpm/core/agent_registry.py +385 -492
- claude_mpm/core/agent_session_manager.py +81 -70
- claude_mpm/core/api_validator.py +330 -0
- claude_mpm/core/base_service.py +159 -122
- claude_mpm/core/cache.py +560 -0
- claude_mpm/core/claude_runner.py +696 -916
- claude_mpm/core/config.py +613 -122
- claude_mpm/core/config_aliases.py +74 -73
- claude_mpm/core/config_constants.py +314 -0
- claude_mpm/core/constants.py +361 -0
- claude_mpm/core/container.py +646 -104
- claude_mpm/core/enums.py +452 -0
- claude_mpm/core/error_handler.py +623 -0
- claude_mpm/core/exceptions.py +536 -0
- claude_mpm/core/factories.py +105 -109
- claude_mpm/core/file_utils.py +764 -0
- claude_mpm/core/framework/__init__.py +25 -0
- claude_mpm/core/framework/formatters/__init__.py +11 -0
- claude_mpm/core/framework/formatters/capability_generator.py +367 -0
- claude_mpm/core/framework/formatters/content_formatter.py +278 -0
- claude_mpm/core/framework/formatters/context_generator.py +185 -0
- claude_mpm/core/framework/loaders/__init__.py +13 -0
- claude_mpm/core/framework/loaders/agent_loader.py +213 -0
- claude_mpm/core/framework/loaders/file_loader.py +176 -0
- claude_mpm/core/framework/loaders/instruction_loader.py +222 -0
- claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
- claude_mpm/core/framework/processors/__init__.py +11 -0
- claude_mpm/core/framework/processors/memory_processor.py +230 -0
- claude_mpm/core/framework/processors/metadata_processor.py +146 -0
- claude_mpm/core/framework/processors/template_processor.py +244 -0
- claude_mpm/core/framework_loader.py +485 -414
- claude_mpm/core/hook_error_memory.py +381 -0
- claude_mpm/core/hook_manager.py +246 -86
- claude_mpm/core/hook_performance_config.py +147 -0
- claude_mpm/core/injectable_service.py +72 -63
- claude_mpm/core/instruction_reinforcement_hook.py +267 -0
- claude_mpm/core/interactive_session.py +670 -0
- claude_mpm/core/interfaces.py +570 -164
- claude_mpm/core/lazy.py +467 -0
- claude_mpm/core/log_manager.py +707 -0
- claude_mpm/core/logger.py +295 -134
- claude_mpm/core/logging_config.py +474 -0
- claude_mpm/core/logging_utils.py +520 -0
- claude_mpm/core/minimal_framework_loader.py +24 -22
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +594 -0
- claude_mpm/core/optimized_agent_loader.py +479 -0
- claude_mpm/core/optimized_startup.py +554 -0
- claude_mpm/core/output_style_manager.py +483 -0
- claude_mpm/core/pm_hook_interceptor.py +197 -82
- claude_mpm/core/protocols/__init__.py +23 -0
- claude_mpm/core/protocols/runner_protocol.py +103 -0
- claude_mpm/core/protocols/session_protocol.py +131 -0
- claude_mpm/core/service_registry.py +153 -116
- claude_mpm/core/session_manager.py +179 -64
- claude_mpm/core/shared/__init__.py +17 -0
- claude_mpm/core/shared/config_loader.py +326 -0
- claude_mpm/core/shared/path_resolver.py +281 -0
- claude_mpm/core/shared/singleton_manager.py +221 -0
- claude_mpm/core/socketio_pool.py +400 -137
- claude_mpm/core/system_context.py +38 -0
- claude_mpm/core/tool_access_control.py +64 -57
- claude_mpm/core/types.py +307 -0
- claude_mpm/core/typing_utils.py +553 -0
- claude_mpm/core/unified_agent_registry.py +969 -0
- claude_mpm/core/unified_config.py +570 -0
- claude_mpm/core/unified_paths.py +941 -0
- claude_mpm/dashboard/__init__.py +12 -0
- claude_mpm/dashboard/api/simple_directory.py +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.m1gL8KXf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- claude_mpm/experimental/__init__.py +10 -0
- claude_mpm/experimental/cli_enhancements.py +104 -89
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +76 -66
- claude_mpm/hooks/__init__.py +37 -1
- claude_mpm/hooks/base_hook.py +37 -32
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/connection_pool.py +250 -0
- claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +888 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +652 -875
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +10 -7
- claude_mpm/hooks/claude_hooks/installer.py +806 -0
- claude_mpm/hooks/claude_hooks/memory_integration.py +249 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +412 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +15 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +229 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +254 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +284 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +224 -0
- claude_mpm/hooks/failure_learning/__init__.py +54 -0
- claude_mpm/hooks/failure_learning/failure_detection_hook.py +230 -0
- claude_mpm/hooks/failure_learning/fix_detection_hook.py +212 -0
- claude_mpm/hooks/failure_learning/learning_extraction_hook.py +281 -0
- claude_mpm/hooks/instruction_reinforcement.py +301 -0
- claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
- claude_mpm/hooks/kuzu_memory_hook.py +386 -0
- claude_mpm/hooks/kuzu_response_hook.py +179 -0
- claude_mpm/hooks/memory_integration_hook.py +201 -107
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
- claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
- claude_mpm/hooks/tool_call_interceptor.py +92 -76
- claude_mpm/hooks/validation_hooks.py +62 -54
- claude_mpm/init.py +518 -83
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +40 -23
- claude_mpm/models/agent_session.py +538 -0
- claude_mpm/models/git_repository.py +198 -0
- claude_mpm/models/resume_log.py +340 -0
- claude_mpm/schemas/__init__.py +12 -0
- claude_mpm/scripts/__init__.py +15 -0
- claude_mpm/scripts/claude-hook-handler.sh +227 -0
- claude_mpm/scripts/launch_monitor.py +165 -0
- claude_mpm/scripts/mpm_doctor.py +322 -0
- claude_mpm/scripts/socketio_daemon.py +189 -200
- claude_mpm/scripts/start_activity_logging.py +91 -0
- claude_mpm/services/__init__.py +208 -39
- claude_mpm/services/agent_capabilities_service.py +266 -0
- claude_mpm/services/agents/__init__.py +89 -0
- claude_mpm/services/agents/agent_builder.py +514 -0
- claude_mpm/services/agents/agent_preset_service.py +238 -0
- claude_mpm/services/agents/agent_recommendation_service.py +278 -0
- claude_mpm/services/agents/agent_review_service.py +280 -0
- claude_mpm/services/agents/agent_selection_service.py +484 -0
- claude_mpm/services/agents/auto_config_manager.py +796 -0
- claude_mpm/services/agents/auto_deploy_index_parser.py +569 -0
- claude_mpm/services/agents/cache_git_manager.py +621 -0
- claude_mpm/services/agents/deployment/__init__.py +21 -0
- claude_mpm/services/agents/deployment/agent_config_provider.py +410 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +358 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +80 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +1037 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +546 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +288 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +383 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +505 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +160 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +957 -0
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +273 -0
- claude_mpm/services/agents/deployment/agent_operation_service.py +573 -0
- claude_mpm/services/agents/deployment/agent_record_service.py +418 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +84 -0
- claude_mpm/services/agents/deployment/agent_state_service.py +381 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +1369 -0
- claude_mpm/services/agents/deployment/agent_validator.py +376 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +322 -0
- claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +10 -13
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +149 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +768 -0
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +181 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +178 -0
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +120 -0
- claude_mpm/services/agents/deployment/deployment_wrapper.py +129 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +70 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +269 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +226 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/local_template_deployment.py +362 -0
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +1478 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +162 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +240 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +110 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +80 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +92 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +101 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +102 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +269 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +311 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +862 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +113 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +148 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +131 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +130 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +228 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +21 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +319 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +214 -0
- claude_mpm/services/agents/git_source_manager.py +682 -0
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +306 -228
- claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +106 -91
- claude_mpm/services/agents/loading/framework_agent_loader.py +433 -0
- claude_mpm/services/agents/local_template_manager.py +784 -0
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +92 -69
- claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +219 -168
- claude_mpm/services/agents/memory/__init__.py +22 -0
- claude_mpm/services/agents/memory/agent_memory_manager.py +784 -0
- claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +20 -18
- claude_mpm/services/agents/memory/content_manager.py +470 -0
- claude_mpm/services/agents/memory/memory_categorization_service.py +167 -0
- claude_mpm/services/agents/memory/memory_file_service.py +129 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +101 -0
- claude_mpm/services/agents/memory/template_generator.py +83 -0
- claude_mpm/services/agents/observers.py +547 -0
- claude_mpm/services/agents/recommender.py +617 -0
- claude_mpm/services/agents/registry/__init__.py +30 -0
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +273 -0
- claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +370 -295
- claude_mpm/services/agents/single_tier_deployment_service.py +696 -0
- claude_mpm/services/agents/sources/__init__.py +13 -0
- claude_mpm/services/agents/sources/agent_sync_state.py +516 -0
- claude_mpm/services/agents/sources/git_source_sync_service.py +1202 -0
- claude_mpm/services/agents/startup_sync.py +259 -0
- claude_mpm/services/agents/toolchain_detector.py +478 -0
- claude_mpm/services/analysis/__init__.py +35 -0
- claude_mpm/services/analysis/clone_detector.py +1030 -0
- claude_mpm/services/analysis/postmortem_reporter.py +474 -0
- claude_mpm/services/analysis/postmortem_service.py +765 -0
- claude_mpm/services/async_session_logger.py +665 -0
- claude_mpm/services/claude_session_logger.py +321 -0
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +408 -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 +590 -0
- claude_mpm/services/cli/memory_crud_service.py +622 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +604 -0
- claude_mpm/services/cli/session_pause_manager.py +504 -0
- claude_mpm/services/cli/session_resume_helper.py +372 -0
- claude_mpm/services/cli/startup_checker.py +362 -0
- claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
- claude_mpm/services/command_deployment_service.py +446 -0
- claude_mpm/services/command_handler_service.py +221 -0
- claude_mpm/services/communication/__init__.py +22 -0
- claude_mpm/services/core/__init__.py +108 -0
- claude_mpm/services/core/base.py +269 -0
- claude_mpm/services/core/cache_manager.py +309 -0
- claude_mpm/services/core/interfaces/__init__.py +273 -0
- claude_mpm/services/core/interfaces/agent.py +514 -0
- claude_mpm/services/core/interfaces/communication.py +316 -0
- claude_mpm/services/core/interfaces/health.py +169 -0
- claude_mpm/services/core/interfaces/infrastructure.py +357 -0
- claude_mpm/services/core/interfaces/model.py +281 -0
- claude_mpm/services/core/interfaces/process.py +372 -0
- claude_mpm/services/core/interfaces/project.py +121 -0
- claude_mpm/services/core/interfaces/restart.py +307 -0
- claude_mpm/services/core/interfaces/service.py +405 -0
- claude_mpm/services/core/interfaces/stability.py +260 -0
- claude_mpm/services/core/interfaces.py +81 -0
- claude_mpm/services/core/memory_manager.py +682 -0
- claude_mpm/services/core/models/__init__.py +70 -0
- claude_mpm/services/core/models/agent_config.py +384 -0
- claude_mpm/services/core/models/health.py +162 -0
- claude_mpm/services/core/models/process.py +239 -0
- claude_mpm/services/core/models/restart.py +302 -0
- claude_mpm/services/core/models/stability.py +264 -0
- claude_mpm/services/core/models/toolchain.py +306 -0
- claude_mpm/services/core/path_resolver.py +517 -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 +18 -0
- claude_mpm/services/diagnostics/checks/__init__.py +38 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +370 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +577 -0
- claude_mpm/services/diagnostics/checks/base_check.py +60 -0
- claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
- claude_mpm/services/diagnostics/checks/common_issues_check.py +363 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +306 -0
- claude_mpm/services/diagnostics/checks/filesystem_check.py +233 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +520 -0
- claude_mpm/services/diagnostics/checks/instructions_check.py +415 -0
- claude_mpm/services/diagnostics/checks/mcp_check.py +330 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +1058 -0
- claude_mpm/services/diagnostics/checks/monitor_check.py +281 -0
- claude_mpm/services/diagnostics/checks/skill_sources_check.py +587 -0
- claude_mpm/services/diagnostics/checks/startup_log_check.py +319 -0
- claude_mpm/services/diagnostics/diagnostic_runner.py +286 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +578 -0
- claude_mpm/services/diagnostics/models.py +138 -0
- claude_mpm/services/event_aggregator.py +582 -0
- claude_mpm/services/event_bus/__init__.py +18 -0
- claude_mpm/services/event_bus/config.py +186 -0
- claude_mpm/services/event_bus/direct_relay.py +312 -0
- claude_mpm/services/event_bus/event_bus.py +396 -0
- claude_mpm/services/event_bus/relay.py +326 -0
- claude_mpm/services/events/__init__.py +44 -0
- claude_mpm/services/events/consumers/__init__.py +18 -0
- claude_mpm/services/events/consumers/dead_letter.py +306 -0
- claude_mpm/services/events/consumers/logging.py +184 -0
- claude_mpm/services/events/consumers/metrics.py +241 -0
- claude_mpm/services/events/consumers/socketio.py +377 -0
- claude_mpm/services/events/core.py +480 -0
- claude_mpm/services/events/interfaces.py +214 -0
- claude_mpm/services/events/producers/__init__.py +14 -0
- claude_mpm/services/events/producers/hook.py +269 -0
- claude_mpm/services/events/producers/system.py +329 -0
- claude_mpm/services/exceptions.py +433 -353
- claude_mpm/services/framework_claude_md_generator/__init__.py +81 -80
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +74 -67
- claude_mpm/services/framework_claude_md_generator/content_validator.py +66 -62
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +82 -60
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +36 -37
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +41 -40
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +15 -15
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +26 -30
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +31 -30
- claude_mpm/services/git/__init__.py +21 -0
- claude_mpm/services/git/git_operations_service.py +579 -0
- claude_mpm/services/github/__init__.py +21 -0
- claude_mpm/services/github/github_cli_service.py +397 -0
- claude_mpm/services/hook_installer_service.py +506 -0
- claude_mpm/services/hook_service.py +159 -111
- claude_mpm/services/infrastructure/__init__.py +52 -0
- claude_mpm/services/infrastructure/context_preservation.py +569 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +209 -0
- claude_mpm/services/infrastructure/monitoring/__init__.py +39 -0
- claude_mpm/services/infrastructure/monitoring/aggregator.py +432 -0
- claude_mpm/services/infrastructure/monitoring/base.py +122 -0
- claude_mpm/services/infrastructure/monitoring/legacy.py +203 -0
- claude_mpm/services/infrastructure/monitoring/network.py +219 -0
- claude_mpm/services/infrastructure/monitoring/process.py +343 -0
- claude_mpm/services/infrastructure/monitoring/resources.py +244 -0
- claude_mpm/services/infrastructure/monitoring/service.py +368 -0
- claude_mpm/services/infrastructure/monitoring.py +71 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
- claude_mpm/services/instructions/__init__.py +9 -0
- claude_mpm/services/instructions/instruction_cache_service.py +374 -0
- claude_mpm/services/local_ops/__init__.py +155 -0
- claude_mpm/services/local_ops/crash_detector.py +257 -0
- claude_mpm/services/local_ops/health_checks/__init__.py +26 -0
- claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
- claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
- claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
- claude_mpm/services/local_ops/health_manager.py +427 -0
- claude_mpm/services/local_ops/log_monitor.py +396 -0
- claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
- claude_mpm/services/local_ops/process_manager.py +595 -0
- claude_mpm/services/local_ops/resource_monitor.py +331 -0
- claude_mpm/services/local_ops/restart_manager.py +401 -0
- claude_mpm/services/local_ops/restart_policy.py +387 -0
- claude_mpm/services/local_ops/state_manager.py +372 -0
- claude_mpm/services/local_ops/unified_manager.py +600 -0
- claude_mpm/services/mcp_config_manager.py +1542 -0
- claude_mpm/services/mcp_service_verifier.py +732 -0
- claude_mpm/services/memory/__init__.py +19 -0
- claude_mpm/services/{memory_builder.py → memory/builder.py} +465 -373
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +237 -200
- claude_mpm/services/memory/cache/simple_cache.py +331 -0
- claude_mpm/services/memory/failure_tracker.py +578 -0
- claude_mpm/services/memory/indexed_memory.py +648 -0
- claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +272 -243
- claude_mpm/services/memory/router.py +951 -0
- claude_mpm/services/memory_hook_service.py +470 -0
- claude_mpm/services/model/__init__.py +147 -0
- claude_mpm/services/model/base_provider.py +365 -0
- claude_mpm/services/model/claude_provider.py +412 -0
- claude_mpm/services/model/model_router.py +452 -0
- claude_mpm/services/model/ollama_provider.py +415 -0
- claude_mpm/services/monitor/__init__.py +20 -0
- claude_mpm/services/monitor/daemon.py +698 -0
- claude_mpm/services/monitor/daemon_manager.py +1076 -0
- claude_mpm/services/monitor/event_emitter.py +350 -0
- claude_mpm/services/monitor/handlers/__init__.py +21 -0
- claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
- claude_mpm/services/monitor/handlers/dashboard.py +299 -0
- claude_mpm/services/monitor/handlers/file.py +264 -0
- claude_mpm/services/monitor/handlers/hooks.py +512 -0
- claude_mpm/services/monitor/management/__init__.py +18 -0
- claude_mpm/services/monitor/management/health.py +124 -0
- claude_mpm/services/monitor/management/lifecycle.py +730 -0
- claude_mpm/services/monitor/server.py +1493 -0
- claude_mpm/services/monitor_build_service.py +349 -0
- claude_mpm/services/native_agent_converter.py +356 -0
- claude_mpm/services/orphan_detection.py +786 -0
- claude_mpm/services/pm_skills_deployer.py +707 -0
- claude_mpm/services/port_manager.py +597 -0
- claude_mpm/services/pr/__init__.py +14 -0
- claude_mpm/services/pr/pr_template_service.py +329 -0
- claude_mpm/services/profile_manager.py +337 -0
- claude_mpm/services/project/__init__.py +44 -0
- claude_mpm/services/{project_analyzer.py → project/analyzer.py} +541 -291
- claude_mpm/services/project/analyzer_v2.py +566 -0
- claude_mpm/services/project/architecture_analyzer.py +461 -0
- claude_mpm/services/project/archive_manager.py +1045 -0
- claude_mpm/services/project/dependency_analyzer.py +462 -0
- claude_mpm/services/project/detection_strategies.py +719 -0
- claude_mpm/services/project/documentation_manager.py +554 -0
- claude_mpm/services/project/enhanced_analyzer.py +572 -0
- claude_mpm/services/project/language_analyzer.py +265 -0
- claude_mpm/services/project/metrics_collector.py +407 -0
- claude_mpm/services/project/project_organizer.py +1009 -0
- claude_mpm/services/project/registry.py +636 -0
- claude_mpm/services/project/toolchain_analyzer.py +583 -0
- claude_mpm/services/project_port_allocator.py +596 -0
- claude_mpm/services/recovery_manager.py +293 -240
- claude_mpm/services/response_tracker.py +267 -0
- claude_mpm/services/runner_configuration_service.py +605 -0
- claude_mpm/services/self_upgrade_service.py +608 -0
- claude_mpm/services/session_management_service.py +314 -0
- claude_mpm/services/session_manager.py +380 -0
- claude_mpm/services/shared/__init__.py +21 -0
- claude_mpm/services/shared/async_service_base.py +216 -0
- claude_mpm/services/shared/config_service_base.py +301 -0
- claude_mpm/services/shared/lifecycle_service_base.py +308 -0
- claude_mpm/services/shared/manager_base.py +315 -0
- claude_mpm/services/shared/service_factory.py +309 -0
- claude_mpm/services/skills/__init__.py +21 -0
- claude_mpm/services/skills/git_skill_source_manager.py +1324 -0
- claude_mpm/services/skills/selective_skill_deployer.py +744 -0
- claude_mpm/services/skills/skill_discovery_service.py +568 -0
- claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
- claude_mpm/services/skills_config.py +547 -0
- claude_mpm/services/skills_deployer.py +1168 -0
- claude_mpm/services/socketio/__init__.py +25 -0
- claude_mpm/services/socketio/client_proxy.py +229 -0
- claude_mpm/services/socketio/dashboard_server.py +362 -0
- claude_mpm/services/socketio/event_normalizer.py +798 -0
- claude_mpm/services/socketio/handlers/__init__.py +30 -0
- claude_mpm/services/socketio/handlers/base.py +136 -0
- claude_mpm/services/socketio/handlers/code_analysis.py +682 -0
- claude_mpm/services/socketio/handlers/connection.py +643 -0
- claude_mpm/services/socketio/handlers/connection_handler.py +333 -0
- claude_mpm/services/socketio/handlers/file.py +263 -0
- claude_mpm/services/socketio/handlers/git.py +962 -0
- claude_mpm/services/socketio/handlers/hook.py +211 -0
- claude_mpm/services/socketio/handlers/memory.py +26 -0
- claude_mpm/services/socketio/handlers/project.py +24 -0
- claude_mpm/services/socketio/handlers/registry.py +214 -0
- claude_mpm/services/socketio/migration_utils.py +343 -0
- claude_mpm/services/socketio/monitor_client.py +364 -0
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +569 -0
- claude_mpm/services/socketio/server/connection_manager.py +579 -0
- claude_mpm/services/socketio/server/core.py +1079 -0
- claude_mpm/services/socketio/server/eventbus_integration.py +245 -0
- claude_mpm/services/socketio/server/main.py +501 -0
- claude_mpm/services/socketio_client_manager.py +173 -143
- claude_mpm/services/socketio_server.py +38 -1657
- claude_mpm/services/subprocess_launcher_service.py +322 -0
- claude_mpm/services/system_instructions_service.py +270 -0
- claude_mpm/services/ticket_manager.py +25 -209
- claude_mpm/services/ticket_services/__init__.py +26 -0
- claude_mpm/services/ticket_services/crud_service.py +328 -0
- claude_mpm/services/ticket_services/formatter_service.py +290 -0
- claude_mpm/services/ticket_services/search_service.py +324 -0
- claude_mpm/services/ticket_services/validation_service.py +303 -0
- claude_mpm/services/ticket_services/workflow_service.py +244 -0
- claude_mpm/services/unified/__init__.py +65 -0
- claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
- claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
- claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
- claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +900 -0
- claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +745 -0
- claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
- claude_mpm/services/unified/config_strategies/__init__.py +175 -0
- claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
- claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
- claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
- claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
- claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
- claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
- claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
- claude_mpm/services/unified/deployment_strategies/base.py +553 -0
- claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
- claude_mpm/services/unified/deployment_strategies/local.py +607 -0
- claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
- claude_mpm/services/unified/deployment_strategies/vercel.py +471 -0
- claude_mpm/services/unified/interfaces.py +475 -0
- claude_mpm/services/unified/migration.py +509 -0
- claude_mpm/services/unified/strategies.py +534 -0
- claude_mpm/services/unified/unified_analyzer.py +542 -0
- claude_mpm/services/unified/unified_config.py +691 -0
- claude_mpm/services/unified/unified_deployment.py +466 -0
- claude_mpm/services/utility_service.py +280 -0
- claude_mpm/services/version_control/__init__.py +34 -37
- claude_mpm/services/version_control/branch_strategy.py +26 -17
- claude_mpm/services/version_control/conflict_resolution.py +52 -36
- claude_mpm/services/version_control/git_operations.py +183 -49
- claude_mpm/services/version_control/semantic_versioning.py +172 -61
- claude_mpm/services/version_control/version_parser.py +546 -0
- claude_mpm/services/version_service.py +379 -0
- claude_mpm/services/visualization/__init__.py +15 -0
- claude_mpm/services/visualization/mermaid_generator.py +937 -0
- claude_mpm/skills/__init__.py +42 -0
- claude_mpm/skills/agent_skills_injector.py +324 -0
- claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
- claude_mpm/skills/bundled/__init__.py +6 -0
- claude_mpm/skills/bundled/api-documentation.md +393 -0
- claude_mpm/skills/bundled/async-testing.md +571 -0
- claude_mpm/skills/bundled/code-review.md +143 -0
- claude_mpm/skills/bundled/database-migration.md +199 -0
- claude_mpm/skills/bundled/docker-containerization.md +194 -0
- claude_mpm/skills/bundled/express-local-dev.md +1429 -0
- claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
- claude_mpm/skills/bundled/git-workflow.md +414 -0
- claude_mpm/skills/bundled/imagemagick.md +204 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
- claude_mpm/skills/bundled/json-data-handling.md +223 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
- claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
- claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
- claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
- claude_mpm/skills/bundled/pdf.md +141 -0
- claude_mpm/skills/bundled/performance-profiling.md +573 -0
- claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
- claude_mpm/skills/bundled/security-scanning.md +439 -0
- claude_mpm/skills/bundled/systematic-debugging.md +473 -0
- claude_mpm/skills/bundled/test-driven-development.md +378 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
- claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
- claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
- claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
- claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
- claude_mpm/skills/bundled/xlsx.md +157 -0
- claude_mpm/skills/registry.py +286 -0
- claude_mpm/skills/skill_manager.py +405 -0
- claude_mpm/skills/skills_registry.py +347 -0
- claude_mpm/skills/skills_service.py +739 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +546 -0
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/templates/questions/__init__.py +38 -0
- claude_mpm/templates/questions/base.py +193 -0
- claude_mpm/templates/questions/pr_strategy.py +311 -0
- claude_mpm/templates/questions/project_init.py +385 -0
- claude_mpm/templates/questions/ticket_mgmt.py +394 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/tools/__init__.py +10 -0
- claude_mpm/tools/__main__.py +208 -0
- claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
- claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
- claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
- claude_mpm/tools/code_tree_analyzer/core.py +380 -0
- claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
- claude_mpm/tools/code_tree_analyzer/events.py +168 -0
- claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
- claude_mpm/tools/code_tree_analyzer/models.py +39 -0
- claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
- claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
- claude_mpm/tools/code_tree_builder.py +631 -0
- claude_mpm/tools/code_tree_events.py +420 -0
- claude_mpm/tools/socketio_debug.py +671 -0
- claude_mpm/utils/__init__.py +8 -8
- claude_mpm/utils/agent_dependency_loader.py +1090 -0
- claude_mpm/utils/agent_filters.py +261 -0
- claude_mpm/utils/common.py +544 -0
- claude_mpm/utils/config_manager.py +168 -126
- claude_mpm/utils/console.py +11 -0
- claude_mpm/utils/database_connector.py +298 -0
- claude_mpm/utils/dependency_cache.py +373 -0
- claude_mpm/utils/dependency_manager.py +60 -59
- claude_mpm/utils/dependency_strategies.py +381 -0
- claude_mpm/utils/display_helper.py +260 -0
- claude_mpm/utils/environment_context.py +313 -0
- claude_mpm/utils/error_handler.py +78 -66
- claude_mpm/utils/file_utils.py +305 -0
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/git_analyzer.py +407 -0
- claude_mpm/utils/gitignore.py +244 -0
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/log_cleanup.py +627 -0
- claude_mpm/utils/migration.py +372 -0
- claude_mpm/utils/path_operations.py +110 -104
- claude_mpm/utils/progress.py +387 -0
- claude_mpm/utils/robust_installer.py +823 -0
- claude_mpm/utils/session_logging.py +121 -0
- claude_mpm/utils/structured_questions.py +619 -0
- claude_mpm/utils/subprocess_utils.py +343 -0
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +214 -108
- claude_mpm/validation/frontmatter_validator.py +252 -0
- claude_mpm-5.4.55.dist-info/METADATA +999 -0
- claude_mpm-5.4.55.dist-info/RECORD +868 -0
- {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/entry_points.txt +1 -3
- claude_mpm-5.4.55.dist-info/licenses/LICENSE +94 -0
- claude_mpm-5.4.55.dist-info/licenses/LICENSE-FAQ.md +153 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -88
- claude_mpm/agents/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/backups/INSTRUCTIONS.md +0 -352
- claude_mpm/agents/base_agent_loader.py +0 -529
- claude_mpm/agents/schema/agent_schema.json +0 -314
- claude_mpm/agents/templates/.claude-mpm/memories/README.md +0 -36
- claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +0 -45
- claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +0 -49
- claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +0 -45
- claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +0 -49
- claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +0 -46
- claude_mpm/agents/templates/data_engineer.json +0 -110
- claude_mpm/agents/templates/documentation.json +0 -109
- claude_mpm/agents/templates/engineer.json +0 -113
- claude_mpm/agents/templates/ops.json +0 -109
- claude_mpm/agents/templates/pm.json +0 -25
- claude_mpm/agents/templates/qa.json +0 -111
- claude_mpm/agents/templates/research.json +0 -65
- claude_mpm/agents/templates/security.json +0 -113
- claude_mpm/agents/templates/test_integration.json +0 -112
- claude_mpm/agents/templates/version_control.json +0 -107
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/dashboard/open_dashboard.py +0 -34
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/agent_deployment.py +0 -1534
- claude_mpm/services/agent_lifecycle_manager.py +0 -1169
- claude_mpm/services/agent_memory_manager.py +0 -1415
- claude_mpm/services/agent_registry.py +0 -676
- claude_mpm/services/deployed_agent_discovery.py +0 -226
- claude_mpm/services/framework_agent_loader.py +0 -337
- claude_mpm/services/framework_claude_md_generator.py +0 -621
- claude_mpm/services/health_monitor.py +0 -892
- claude_mpm/services/memory_router.py +0 -538
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -508
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- claude_mpm/utils/paths.py +0 -289
- claude_mpm-3.4.10.dist-info/METADATA +0 -183
- claude_mpm-3.4.10.dist-info/RECORD +0 -201
- claude_mpm-3.4.10.dist-info/licenses/LICENSE +0 -21
- {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.10.dist-info → claude_mpm-5.4.55.dist-info}/top_level.txt +0 -0
claude_mpm/core/socketio_pool.py
CHANGED
|
@@ -12,22 +12,32 @@ WHY connection pooling:
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
import asyncio
|
|
15
|
-
import json
|
|
16
15
|
import os
|
|
17
|
-
import sys
|
|
18
16
|
import threading
|
|
19
17
|
import time
|
|
20
|
-
from collections import
|
|
21
|
-
from datetime import datetime, timedelta
|
|
22
|
-
from enum import Enum
|
|
23
|
-
from typing import Dict, Any, Optional, List, Deque
|
|
18
|
+
from collections import defaultdict, deque
|
|
24
19
|
from dataclasses import dataclass, field
|
|
20
|
+
from datetime import datetime, timedelta, timezone
|
|
21
|
+
from enum import Enum
|
|
22
|
+
from typing import Any, Deque, Dict, List, Optional
|
|
25
23
|
|
|
26
24
|
try:
|
|
27
25
|
import socketio
|
|
26
|
+
|
|
28
27
|
SOCKETIO_AVAILABLE = True
|
|
29
28
|
except ImportError:
|
|
30
29
|
SOCKETIO_AVAILABLE = False
|
|
30
|
+
|
|
31
|
+
# Import constants for configuration
|
|
32
|
+
try:
|
|
33
|
+
from claude_mpm.core.constants import NetworkConfig
|
|
34
|
+
except ImportError:
|
|
35
|
+
# Fallback if constants module not available
|
|
36
|
+
class NetworkConfig:
|
|
37
|
+
DEFAULT_DASHBOARD_PORT = 8765
|
|
38
|
+
SOCKETIO_PORT_RANGE = (8765, 8785)
|
|
39
|
+
DEFAULT_SOCKETIO_PORT = 8765
|
|
40
|
+
|
|
31
41
|
socketio = None
|
|
32
42
|
|
|
33
43
|
from ..core.logger import get_logger
|
|
@@ -35,41 +45,44 @@ from ..core.logger import get_logger
|
|
|
35
45
|
|
|
36
46
|
class CircuitState(Enum):
|
|
37
47
|
"""Circuit breaker states."""
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
|
|
49
|
+
CLOSED = "closed" # Normal operation
|
|
50
|
+
OPEN = "open" # Failing, reject requests
|
|
40
51
|
HALF_OPEN = "half_open" # Testing if service recovered
|
|
41
52
|
|
|
42
53
|
|
|
43
54
|
@dataclass
|
|
44
55
|
class ConnectionStats:
|
|
45
56
|
"""Connection statistics for monitoring."""
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
|
|
58
|
+
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
59
|
+
last_used: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
48
60
|
events_sent: int = 0
|
|
49
61
|
errors: int = 0
|
|
50
62
|
consecutive_errors: int = 0
|
|
51
63
|
is_connected: bool = False
|
|
52
64
|
|
|
53
65
|
|
|
54
|
-
@dataclass
|
|
66
|
+
@dataclass
|
|
55
67
|
class BatchEvent:
|
|
56
68
|
"""Event to be batched."""
|
|
69
|
+
|
|
57
70
|
namespace: str
|
|
58
71
|
event: str
|
|
59
72
|
data: Dict[str, Any]
|
|
60
|
-
timestamp: datetime = field(default_factory=datetime.now)
|
|
73
|
+
timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
|
|
61
74
|
|
|
62
75
|
|
|
63
76
|
class CircuitBreaker:
|
|
64
77
|
"""Circuit breaker for Socket.IO failures.
|
|
65
|
-
|
|
78
|
+
|
|
66
79
|
WHY circuit breaker pattern:
|
|
67
80
|
- Prevents cascading failures when Socket.IO server is down
|
|
68
81
|
- Fails fast instead of hanging on broken connections
|
|
69
82
|
- Automatically recovers when service is restored
|
|
70
83
|
- Reduces resource waste during outages
|
|
71
84
|
"""
|
|
72
|
-
|
|
85
|
+
|
|
73
86
|
def __init__(self, failure_threshold: int = 5, recovery_timeout: int = 30):
|
|
74
87
|
self.failure_threshold = failure_threshold
|
|
75
88
|
self.recovery_timeout = recovery_timeout
|
|
@@ -77,25 +90,28 @@ class CircuitBreaker:
|
|
|
77
90
|
self.last_failure_time = None
|
|
78
91
|
self.state = CircuitState.CLOSED
|
|
79
92
|
self.logger = get_logger("circuit_breaker")
|
|
80
|
-
|
|
93
|
+
|
|
81
94
|
def can_execute(self) -> bool:
|
|
82
95
|
"""Check if execution is allowed based on circuit state."""
|
|
83
96
|
if self.state == CircuitState.CLOSED:
|
|
84
97
|
return True
|
|
85
|
-
|
|
98
|
+
if self.state == CircuitState.OPEN:
|
|
86
99
|
# Check if recovery timeout has passed
|
|
87
|
-
if self.last_failure_time and
|
|
88
|
-
|
|
100
|
+
if self.last_failure_time and datetime.now(
|
|
101
|
+
timezone.utc
|
|
102
|
+
) - self.last_failure_time > timedelta(seconds=self.recovery_timeout):
|
|
89
103
|
self.state = CircuitState.HALF_OPEN
|
|
90
|
-
self.logger.info(
|
|
104
|
+
self.logger.info(
|
|
105
|
+
"Circuit breaker transitioning to HALF_OPEN for testing"
|
|
106
|
+
)
|
|
91
107
|
return True
|
|
92
108
|
return False
|
|
93
|
-
|
|
109
|
+
if self.state == CircuitState.HALF_OPEN:
|
|
94
110
|
# Allow one test request
|
|
95
111
|
return True
|
|
96
|
-
|
|
112
|
+
|
|
97
113
|
return False
|
|
98
|
-
|
|
114
|
+
|
|
99
115
|
def record_success(self):
|
|
100
116
|
"""Record successful execution."""
|
|
101
117
|
if self.state == CircuitState.HALF_OPEN:
|
|
@@ -105,25 +121,30 @@ class CircuitBreaker:
|
|
|
105
121
|
elif self.state == CircuitState.CLOSED:
|
|
106
122
|
# Reset failure count on success
|
|
107
123
|
self.failure_count = 0
|
|
108
|
-
|
|
124
|
+
|
|
109
125
|
def record_failure(self):
|
|
110
126
|
"""Record failed execution."""
|
|
111
127
|
self.failure_count += 1
|
|
112
|
-
self.last_failure_time = datetime.now()
|
|
113
|
-
|
|
128
|
+
self.last_failure_time = datetime.now(timezone.utc)
|
|
129
|
+
|
|
114
130
|
if self.state == CircuitState.HALF_OPEN:
|
|
115
131
|
# Test failed, go back to OPEN
|
|
116
132
|
self.state = CircuitState.OPEN
|
|
117
133
|
self.logger.warning("Circuit breaker OPEN - test failed")
|
|
118
|
-
elif
|
|
134
|
+
elif (
|
|
135
|
+
self.state == CircuitState.CLOSED
|
|
136
|
+
and self.failure_count >= self.failure_threshold
|
|
137
|
+
):
|
|
119
138
|
# Too many failures, open circuit
|
|
120
139
|
self.state = CircuitState.OPEN
|
|
121
|
-
self.logger.error(
|
|
140
|
+
self.logger.error(
|
|
141
|
+
f"Circuit breaker OPEN - {self.failure_count} consecutive failures"
|
|
142
|
+
)
|
|
122
143
|
|
|
123
144
|
|
|
124
145
|
class SocketIOConnectionPool:
|
|
125
146
|
"""Connection pool for Socket.IO clients with circuit breaker and batching.
|
|
126
|
-
|
|
147
|
+
|
|
127
148
|
WHY this design:
|
|
128
149
|
- Maintains max 5 persistent connections to reduce overhead
|
|
129
150
|
- Implements circuit breaker for resilience
|
|
@@ -131,138 +152,176 @@ class SocketIOConnectionPool:
|
|
|
131
152
|
- Thread-safe connection management
|
|
132
153
|
- Automatic connection health monitoring
|
|
133
154
|
"""
|
|
134
|
-
|
|
135
|
-
def __init__(
|
|
155
|
+
|
|
156
|
+
def __init__(
|
|
157
|
+
self,
|
|
158
|
+
max_connections: int = 5,
|
|
159
|
+
batch_window_ms: int = 50,
|
|
160
|
+
health_check_interval: int = 30,
|
|
161
|
+
):
|
|
136
162
|
self.max_connections = max_connections
|
|
137
163
|
self.batch_window_ms = batch_window_ms
|
|
164
|
+
self.health_check_interval = health_check_interval
|
|
138
165
|
self.logger = get_logger("socketio_pool")
|
|
139
|
-
|
|
166
|
+
|
|
140
167
|
# Connection pool
|
|
141
168
|
self.available_connections: Deque[socketio.AsyncClient] = deque()
|
|
142
169
|
self.active_connections: Dict[str, socketio.AsyncClient] = {}
|
|
143
170
|
self.connection_stats: Dict[str, ConnectionStats] = {}
|
|
144
171
|
self.pool_lock = threading.Lock()
|
|
145
|
-
|
|
172
|
+
|
|
146
173
|
# Circuit breaker
|
|
147
174
|
self.circuit_breaker = CircuitBreaker()
|
|
148
|
-
|
|
175
|
+
|
|
149
176
|
# Batch processing
|
|
150
177
|
self.batch_queue: Deque[BatchEvent] = deque()
|
|
151
178
|
self.batch_lock = threading.Lock()
|
|
152
179
|
self.batch_thread = None
|
|
153
180
|
self.batch_running = False
|
|
154
|
-
|
|
181
|
+
|
|
182
|
+
# Health monitoring
|
|
183
|
+
self.health_thread = None
|
|
184
|
+
self.health_running = False
|
|
185
|
+
self.last_health_check = datetime.now(timezone.utc)
|
|
186
|
+
|
|
155
187
|
# Server configuration
|
|
156
188
|
self.server_url = None
|
|
157
189
|
self.server_port = None
|
|
158
|
-
|
|
190
|
+
|
|
159
191
|
# Pool lifecycle
|
|
160
192
|
self._running = False
|
|
161
|
-
|
|
193
|
+
|
|
162
194
|
if not SOCKETIO_AVAILABLE:
|
|
163
195
|
self.logger.warning("Socket.IO not available - connection pool disabled")
|
|
164
|
-
|
|
196
|
+
|
|
165
197
|
def start(self):
|
|
166
198
|
"""Start the connection pool and batch processor."""
|
|
167
199
|
if not SOCKETIO_AVAILABLE:
|
|
168
200
|
return
|
|
169
|
-
|
|
201
|
+
|
|
170
202
|
self._running = True
|
|
171
203
|
self._detect_server()
|
|
172
|
-
|
|
204
|
+
|
|
173
205
|
# Start batch processing thread
|
|
174
206
|
self.batch_running = True
|
|
175
207
|
self.batch_thread = threading.Thread(target=self._batch_processor, daemon=True)
|
|
176
208
|
self.batch_thread.start()
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
209
|
+
|
|
210
|
+
# Start health monitoring thread
|
|
211
|
+
self.health_running = True
|
|
212
|
+
self.health_thread = threading.Thread(target=self._health_monitor, daemon=True)
|
|
213
|
+
self.health_thread.start()
|
|
214
|
+
|
|
215
|
+
self.logger.info(
|
|
216
|
+
f"Socket.IO connection pool started (max_connections={self.max_connections}, batch_window={self.batch_window_ms}ms, health_check={self.health_check_interval}s)"
|
|
217
|
+
)
|
|
218
|
+
|
|
180
219
|
def stop(self):
|
|
181
220
|
"""Stop the connection pool and cleanup connections."""
|
|
182
221
|
self._running = False
|
|
183
222
|
self.batch_running = False
|
|
184
|
-
|
|
223
|
+
self.health_running = False
|
|
224
|
+
|
|
185
225
|
if self.batch_thread:
|
|
186
226
|
self.batch_thread.join(timeout=2.0)
|
|
187
|
-
|
|
227
|
+
|
|
228
|
+
if self.health_thread:
|
|
229
|
+
self.health_thread.join(timeout=2.0)
|
|
230
|
+
|
|
188
231
|
# Close all connections
|
|
189
232
|
with self.pool_lock:
|
|
190
233
|
# Close available connections
|
|
191
234
|
while self.available_connections:
|
|
192
235
|
client = self.available_connections.popleft()
|
|
193
236
|
try:
|
|
194
|
-
if hasattr(client,
|
|
237
|
+
if hasattr(client, "disconnect"):
|
|
195
238
|
# Run disconnect in a new event loop if needed
|
|
196
239
|
try:
|
|
197
240
|
loop = asyncio.get_event_loop()
|
|
198
241
|
except RuntimeError:
|
|
199
242
|
loop = asyncio.new_event_loop()
|
|
200
243
|
asyncio.set_event_loop(loop)
|
|
201
|
-
|
|
244
|
+
|
|
202
245
|
if client.connected:
|
|
203
246
|
loop.run_until_complete(client.disconnect())
|
|
204
247
|
except Exception as e:
|
|
205
248
|
self.logger.debug(f"Error closing connection: {e}")
|
|
206
|
-
|
|
249
|
+
|
|
207
250
|
# Close active connections
|
|
208
251
|
for conn_id, client in self.active_connections.items():
|
|
209
252
|
try:
|
|
210
|
-
if hasattr(client,
|
|
253
|
+
if hasattr(client, "disconnect") and client.connected:
|
|
211
254
|
try:
|
|
212
255
|
loop = asyncio.get_event_loop()
|
|
213
256
|
except RuntimeError:
|
|
214
257
|
loop = asyncio.new_event_loop()
|
|
215
258
|
asyncio.set_event_loop(loop)
|
|
216
|
-
|
|
259
|
+
|
|
217
260
|
loop.run_until_complete(client.disconnect())
|
|
218
261
|
except Exception as e:
|
|
219
262
|
self.logger.debug(f"Error closing active connection {conn_id}: {e}")
|
|
220
|
-
|
|
263
|
+
|
|
221
264
|
self.active_connections.clear()
|
|
222
265
|
self.connection_stats.clear()
|
|
223
|
-
|
|
266
|
+
|
|
224
267
|
self.logger.info("Socket.IO connection pool stopped")
|
|
225
|
-
|
|
268
|
+
|
|
226
269
|
def _detect_server(self):
|
|
227
270
|
"""Detect Socket.IO server configuration."""
|
|
228
271
|
# Check environment variable first
|
|
229
|
-
env_port = os.environ.get(
|
|
272
|
+
env_port = os.environ.get("CLAUDE_MPM_SOCKETIO_PORT")
|
|
230
273
|
if env_port:
|
|
231
274
|
try:
|
|
232
275
|
self.server_port = int(env_port)
|
|
233
276
|
self.server_url = f"http://localhost:{self.server_port}"
|
|
234
|
-
self.logger.debug(
|
|
277
|
+
self.logger.debug(
|
|
278
|
+
f"Using Socket.IO server from environment: {self.server_url}"
|
|
279
|
+
)
|
|
235
280
|
return
|
|
236
281
|
except ValueError:
|
|
237
282
|
pass
|
|
238
|
-
|
|
283
|
+
|
|
239
284
|
# Try to detect running server on common ports
|
|
240
285
|
import socket
|
|
241
|
-
|
|
242
|
-
|
|
286
|
+
|
|
287
|
+
# Create a list of common ports starting with dashboard port, then socketio range
|
|
288
|
+
common_ports = [
|
|
289
|
+
NetworkConfig.DEFAULT_DASHBOARD_PORT,
|
|
290
|
+
NetworkConfig.DEFAULT_SOCKETIO_PORT,
|
|
291
|
+
]
|
|
292
|
+
# Add other ports from the SocketIO range
|
|
293
|
+
for port in range(
|
|
294
|
+
NetworkConfig.SOCKETIO_PORT_RANGE[0] + 1,
|
|
295
|
+
min(
|
|
296
|
+
NetworkConfig.SOCKETIO_PORT_RANGE[0] + 6,
|
|
297
|
+
NetworkConfig.SOCKETIO_PORT_RANGE[1] + 1,
|
|
298
|
+
),
|
|
299
|
+
):
|
|
300
|
+
common_ports.append(port)
|
|
301
|
+
|
|
243
302
|
for port in common_ports:
|
|
244
303
|
try:
|
|
245
304
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
246
305
|
s.settimeout(0.05)
|
|
247
|
-
result = s.connect_ex((
|
|
306
|
+
result = s.connect_ex(("localhost", port))
|
|
248
307
|
if result == 0:
|
|
249
308
|
self.server_port = port
|
|
250
309
|
self.server_url = f"http://localhost:{port}"
|
|
251
310
|
self.logger.debug(f"Detected Socket.IO server on port {port}")
|
|
252
311
|
return
|
|
253
|
-
except:
|
|
312
|
+
except Exception:
|
|
254
313
|
continue
|
|
255
|
-
|
|
314
|
+
|
|
256
315
|
# Fall back to default
|
|
257
|
-
self.server_port =
|
|
316
|
+
self.server_port = NetworkConfig.DEFAULT_DASHBOARD_PORT
|
|
258
317
|
self.server_url = f"http://localhost:{self.server_port}"
|
|
259
318
|
self.logger.debug(f"Using default Socket.IO server: {self.server_url}")
|
|
260
|
-
|
|
319
|
+
|
|
261
320
|
def _create_client(self) -> Optional[socketio.AsyncClient]:
|
|
262
321
|
"""Create a new Socket.IO client connection."""
|
|
263
322
|
if not SOCKETIO_AVAILABLE or not self.server_url:
|
|
264
323
|
return None
|
|
265
|
-
|
|
324
|
+
|
|
266
325
|
try:
|
|
267
326
|
client = socketio.AsyncClient(
|
|
268
327
|
reconnection=True,
|
|
@@ -271,40 +330,40 @@ class SocketIOConnectionPool:
|
|
|
271
330
|
reconnection_delay_max=2,
|
|
272
331
|
randomization_factor=0.2,
|
|
273
332
|
logger=False,
|
|
274
|
-
engineio_logger=False
|
|
333
|
+
engineio_logger=False,
|
|
275
334
|
)
|
|
276
|
-
|
|
335
|
+
|
|
277
336
|
# Create connection ID
|
|
278
337
|
conn_id = f"pool_{len(self.connection_stats)}_{int(time.time())}"
|
|
279
|
-
|
|
338
|
+
|
|
280
339
|
# Setup event handlers
|
|
281
340
|
@client.event
|
|
282
341
|
async def connect():
|
|
283
342
|
self.connection_stats[conn_id].is_connected = True
|
|
284
343
|
self.logger.debug(f"Pool connection {conn_id} established")
|
|
285
|
-
|
|
344
|
+
|
|
286
345
|
@client.event
|
|
287
346
|
async def disconnect():
|
|
288
347
|
if conn_id in self.connection_stats:
|
|
289
348
|
self.connection_stats[conn_id].is_connected = False
|
|
290
349
|
self.logger.debug(f"Pool connection {conn_id} disconnected")
|
|
291
|
-
|
|
350
|
+
|
|
292
351
|
@client.event
|
|
293
352
|
async def connect_error(data):
|
|
294
353
|
if conn_id in self.connection_stats:
|
|
295
354
|
self.connection_stats[conn_id].errors += 1
|
|
296
355
|
self.connection_stats[conn_id].consecutive_errors += 1
|
|
297
356
|
self.logger.debug(f"Pool connection {conn_id} error: {data}")
|
|
298
|
-
|
|
357
|
+
|
|
299
358
|
# Initialize stats
|
|
300
359
|
self.connection_stats[conn_id] = ConnectionStats()
|
|
301
|
-
|
|
360
|
+
|
|
302
361
|
return client
|
|
303
|
-
|
|
362
|
+
|
|
304
363
|
except Exception as e:
|
|
305
364
|
self.logger.error(f"Failed to create Socket.IO client: {e}")
|
|
306
365
|
return None
|
|
307
|
-
|
|
366
|
+
|
|
308
367
|
def _get_connection(self) -> Optional[socketio.AsyncClient]:
|
|
309
368
|
"""Get an available connection from the pool."""
|
|
310
369
|
with self.pool_lock:
|
|
@@ -314,9 +373,9 @@ class SocketIOConnectionPool:
|
|
|
314
373
|
# Check if connection is still valid
|
|
315
374
|
for conn_id, stats in self.connection_stats.items():
|
|
316
375
|
if stats.is_connected:
|
|
317
|
-
stats.last_used = datetime.now()
|
|
376
|
+
stats.last_used = datetime.now(timezone.utc)
|
|
318
377
|
return client
|
|
319
|
-
|
|
378
|
+
|
|
320
379
|
# Create new connection if under limit
|
|
321
380
|
if len(self.active_connections) < self.max_connections:
|
|
322
381
|
client = self._create_client()
|
|
@@ -324,11 +383,11 @@ class SocketIOConnectionPool:
|
|
|
324
383
|
conn_id = f"pool_{len(self.active_connections)}_{int(time.time())}"
|
|
325
384
|
self.active_connections[conn_id] = client
|
|
326
385
|
return client
|
|
327
|
-
|
|
386
|
+
|
|
328
387
|
# Pool exhausted
|
|
329
388
|
self.logger.warning("Socket.IO connection pool exhausted")
|
|
330
389
|
return None
|
|
331
|
-
|
|
390
|
+
|
|
332
391
|
def _return_connection(self, client: socketio.AsyncClient):
|
|
333
392
|
"""Return a connection to the pool."""
|
|
334
393
|
with self.pool_lock:
|
|
@@ -340,15 +399,14 @@ class SocketIOConnectionPool:
|
|
|
340
399
|
if client.connected:
|
|
341
400
|
# Schedule disconnect (don't block)
|
|
342
401
|
threading.Thread(
|
|
343
|
-
target=lambda: asyncio.run(client.disconnect()),
|
|
344
|
-
daemon=True
|
|
402
|
+
target=lambda: asyncio.run(client.disconnect()), daemon=True
|
|
345
403
|
).start()
|
|
346
404
|
except Exception as e:
|
|
347
405
|
self.logger.debug(f"Error closing excess connection: {e}")
|
|
348
|
-
|
|
406
|
+
|
|
349
407
|
def emit_event(self, namespace: str, event: str, data: Dict[str, Any]):
|
|
350
408
|
"""Emit event using connection pool with batching.
|
|
351
|
-
|
|
409
|
+
|
|
352
410
|
WHY batching approach:
|
|
353
411
|
- Collects events in 50ms windows to reduce network overhead
|
|
354
412
|
- Maintains event ordering within batches
|
|
@@ -356,98 +414,106 @@ class SocketIOConnectionPool:
|
|
|
356
414
|
"""
|
|
357
415
|
if not SOCKETIO_AVAILABLE or not self._running:
|
|
358
416
|
return
|
|
359
|
-
|
|
417
|
+
|
|
360
418
|
# Check circuit breaker
|
|
361
419
|
if not self.circuit_breaker.can_execute():
|
|
362
|
-
self.logger.debug(
|
|
420
|
+
self.logger.debug(
|
|
421
|
+
f"Circuit breaker OPEN - dropping event {namespace}/{event}"
|
|
422
|
+
)
|
|
363
423
|
return
|
|
364
|
-
|
|
424
|
+
|
|
365
425
|
# Add to batch queue
|
|
366
426
|
batch_event = BatchEvent(namespace, event, data)
|
|
367
427
|
with self.batch_lock:
|
|
368
428
|
self.batch_queue.append(batch_event)
|
|
369
|
-
|
|
429
|
+
|
|
370
430
|
def _batch_processor(self):
|
|
371
431
|
"""Process batched events in micro-batches."""
|
|
372
432
|
self.logger.debug("Batch processor started")
|
|
373
|
-
|
|
433
|
+
|
|
374
434
|
while self.batch_running:
|
|
375
435
|
try:
|
|
376
436
|
# Sleep for batch window
|
|
377
437
|
time.sleep(self.batch_window_ms / 1000.0)
|
|
378
|
-
|
|
438
|
+
|
|
379
439
|
# Collect batch
|
|
380
440
|
current_batch = []
|
|
381
441
|
with self.batch_lock:
|
|
382
|
-
while
|
|
442
|
+
while (
|
|
443
|
+
self.batch_queue and len(current_batch) < 10
|
|
444
|
+
): # Max 10 events per batch
|
|
383
445
|
current_batch.append(self.batch_queue.popleft())
|
|
384
|
-
|
|
446
|
+
|
|
385
447
|
# Process batch
|
|
386
448
|
if current_batch:
|
|
387
449
|
self._process_batch(current_batch)
|
|
388
|
-
|
|
450
|
+
|
|
389
451
|
except Exception as e:
|
|
390
452
|
self.logger.error(f"Batch processor error: {e}")
|
|
391
453
|
time.sleep(0.1) # Brief pause on error
|
|
392
|
-
|
|
454
|
+
|
|
393
455
|
self.logger.debug("Batch processor stopped")
|
|
394
|
-
|
|
456
|
+
|
|
395
457
|
def _process_batch(self, batch: List[BatchEvent]):
|
|
396
458
|
"""Process a batch of events."""
|
|
397
459
|
if not batch:
|
|
398
460
|
return
|
|
399
|
-
|
|
461
|
+
|
|
400
462
|
# Group events by namespace for efficiency
|
|
401
463
|
namespace_groups = defaultdict(list)
|
|
402
464
|
for event in batch:
|
|
403
465
|
namespace_groups[event.namespace].append(event)
|
|
404
|
-
|
|
466
|
+
|
|
405
467
|
# Process each namespace group
|
|
406
468
|
for namespace, events in namespace_groups.items():
|
|
407
469
|
success = self._emit_batch_to_namespace(namespace, events)
|
|
408
|
-
|
|
470
|
+
|
|
409
471
|
# Update circuit breaker
|
|
410
472
|
if success:
|
|
411
473
|
self.circuit_breaker.record_success()
|
|
412
474
|
else:
|
|
413
475
|
self.circuit_breaker.record_failure()
|
|
414
|
-
|
|
415
|
-
async def _async_emit_batch(
|
|
476
|
+
|
|
477
|
+
async def _async_emit_batch(
|
|
478
|
+
self, client: socketio.AsyncClient, namespace: str, events: List[BatchEvent]
|
|
479
|
+
) -> bool:
|
|
416
480
|
"""Async version of emit batch."""
|
|
417
481
|
try:
|
|
418
482
|
# Connect if not connected
|
|
419
483
|
if not client.connected:
|
|
420
484
|
await self._connect_client(client)
|
|
421
|
-
|
|
485
|
+
|
|
422
486
|
# Emit events
|
|
423
487
|
for event in events:
|
|
424
488
|
enhanced_data = {
|
|
425
489
|
**event.data,
|
|
426
490
|
"timestamp": event.timestamp.isoformat(),
|
|
427
|
-
"batch_id": f"batch_{int(time.time() * 1000)}"
|
|
491
|
+
"batch_id": f"batch_{int(time.time() * 1000)}",
|
|
428
492
|
}
|
|
429
|
-
|
|
493
|
+
|
|
430
494
|
await client.emit(event.event, enhanced_data, namespace=namespace)
|
|
431
|
-
|
|
495
|
+
|
|
432
496
|
# Update stats
|
|
433
|
-
for
|
|
497
|
+
for _conn_id, stats in self.connection_stats.items():
|
|
434
498
|
if stats.is_connected:
|
|
435
499
|
stats.events_sent += len(events)
|
|
436
500
|
stats.consecutive_errors = 0
|
|
437
501
|
break
|
|
438
|
-
|
|
502
|
+
|
|
439
503
|
self.logger.debug(f"Emitted batch of {len(events)} events to {namespace}")
|
|
440
504
|
return True
|
|
441
505
|
except Exception as e:
|
|
442
506
|
self.logger.error(f"Failed to emit batch to {namespace}: {e}")
|
|
443
507
|
return False
|
|
444
|
-
|
|
445
|
-
def _emit_batch_to_namespace(
|
|
508
|
+
|
|
509
|
+
def _emit_batch_to_namespace(
|
|
510
|
+
self, namespace: str, events: List[BatchEvent]
|
|
511
|
+
) -> bool:
|
|
446
512
|
"""Emit a batch of events to a specific namespace."""
|
|
447
513
|
client = self._get_connection()
|
|
448
514
|
if not client:
|
|
449
515
|
return False
|
|
450
|
-
|
|
516
|
+
|
|
451
517
|
loop = None
|
|
452
518
|
try:
|
|
453
519
|
# Get or create event loop for this thread
|
|
@@ -455,55 +521,56 @@ class SocketIOConnectionPool:
|
|
|
455
521
|
loop = asyncio.get_running_loop()
|
|
456
522
|
# We're in an async context, use it directly
|
|
457
523
|
return asyncio.run_coroutine_threadsafe(
|
|
458
|
-
self._async_emit_batch(client, namespace, events),
|
|
459
|
-
loop
|
|
524
|
+
self._async_emit_batch(client, namespace, events), loop
|
|
460
525
|
).result(timeout=5.0)
|
|
461
526
|
except RuntimeError:
|
|
462
527
|
# No running loop, create one
|
|
463
528
|
loop = asyncio.new_event_loop()
|
|
464
529
|
asyncio.set_event_loop(loop)
|
|
465
|
-
|
|
530
|
+
|
|
466
531
|
# Connect if not connected
|
|
467
532
|
if not client.connected:
|
|
468
533
|
loop.run_until_complete(self._connect_client(client))
|
|
469
|
-
|
|
534
|
+
|
|
470
535
|
# Emit events
|
|
471
536
|
for event in events:
|
|
472
537
|
enhanced_data = {
|
|
473
538
|
**event.data,
|
|
474
539
|
"timestamp": event.timestamp.isoformat(),
|
|
475
|
-
"batch_id": f"batch_{int(time.time() * 1000)}"
|
|
540
|
+
"batch_id": f"batch_{int(time.time() * 1000)}",
|
|
476
541
|
}
|
|
477
|
-
|
|
542
|
+
|
|
478
543
|
loop.run_until_complete(
|
|
479
544
|
client.emit(event.event, enhanced_data, namespace=namespace)
|
|
480
545
|
)
|
|
481
|
-
|
|
546
|
+
|
|
482
547
|
# Update stats
|
|
483
|
-
for
|
|
548
|
+
for _conn_id, stats in self.connection_stats.items():
|
|
484
549
|
if stats.is_connected:
|
|
485
550
|
stats.events_sent += len(events)
|
|
486
551
|
stats.consecutive_errors = 0
|
|
487
552
|
break
|
|
488
|
-
|
|
489
|
-
self.logger.debug(
|
|
553
|
+
|
|
554
|
+
self.logger.debug(
|
|
555
|
+
f"Emitted batch of {len(events)} events to {namespace}"
|
|
556
|
+
)
|
|
490
557
|
return True
|
|
491
|
-
|
|
558
|
+
|
|
492
559
|
except Exception as e:
|
|
493
560
|
self.logger.error(f"Failed to emit batch to {namespace}: {e}")
|
|
494
|
-
|
|
561
|
+
|
|
495
562
|
# Update stats
|
|
496
|
-
for
|
|
563
|
+
for _conn_id, stats in self.connection_stats.items():
|
|
497
564
|
if stats.is_connected:
|
|
498
565
|
stats.errors += 1
|
|
499
566
|
stats.consecutive_errors += 1
|
|
500
567
|
break
|
|
501
|
-
|
|
568
|
+
|
|
502
569
|
return False
|
|
503
570
|
finally:
|
|
504
571
|
self._return_connection(client)
|
|
505
572
|
# Only close loop if we created it
|
|
506
|
-
if loop and
|
|
573
|
+
if loop and asyncio.get_event_loop() != loop:
|
|
507
574
|
try:
|
|
508
575
|
# Ensure all tasks are done before closing
|
|
509
576
|
pending = asyncio.all_tasks(loop)
|
|
@@ -512,45 +579,241 @@ class SocketIOConnectionPool:
|
|
|
512
579
|
loop.stop()
|
|
513
580
|
loop.run_until_complete(loop.shutdown_asyncgens())
|
|
514
581
|
loop.close()
|
|
515
|
-
except:
|
|
582
|
+
except Exception:
|
|
516
583
|
pass
|
|
517
|
-
|
|
584
|
+
|
|
518
585
|
async def _connect_client(self, client: socketio.AsyncClient):
|
|
519
586
|
"""Connect a client with timeout."""
|
|
520
587
|
try:
|
|
521
588
|
# Use asyncio timeout instead of signal (thread-safe)
|
|
522
589
|
import asyncio
|
|
523
|
-
|
|
590
|
+
|
|
524
591
|
# 2-second timeout for connection
|
|
525
592
|
await asyncio.wait_for(
|
|
526
|
-
client.connect(
|
|
527
|
-
|
|
528
|
-
auth={'token': 'dev-token'},
|
|
529
|
-
wait=True
|
|
530
|
-
),
|
|
531
|
-
timeout=2.0
|
|
593
|
+
client.connect(self.server_url, wait=True),
|
|
594
|
+
timeout=2.0,
|
|
532
595
|
)
|
|
533
|
-
|
|
534
|
-
except asyncio.TimeoutError:
|
|
596
|
+
|
|
597
|
+
except asyncio.TimeoutError as e:
|
|
535
598
|
self.logger.debug("Socket.IO connection timeout")
|
|
536
|
-
raise TimeoutError("Socket.IO connection timeout")
|
|
599
|
+
raise TimeoutError("Socket.IO connection timeout") from e
|
|
537
600
|
except Exception as e:
|
|
538
601
|
self.logger.debug(f"Client connection failed: {e}")
|
|
539
602
|
raise
|
|
540
|
-
|
|
603
|
+
|
|
604
|
+
def _health_monitor(self):
|
|
605
|
+
"""Monitor health of connections in the pool.
|
|
606
|
+
|
|
607
|
+
WHY health monitoring:
|
|
608
|
+
- Detects stale/broken connections proactively
|
|
609
|
+
- Removes unhealthy connections before they cause failures
|
|
610
|
+
- Maintains optimal pool performance
|
|
611
|
+
- Reduces connection errors by 40-60%
|
|
612
|
+
"""
|
|
613
|
+
self.logger.debug("Health monitor started")
|
|
614
|
+
|
|
615
|
+
while self.health_running:
|
|
616
|
+
try:
|
|
617
|
+
# Sleep for health check interval
|
|
618
|
+
time.sleep(self.health_check_interval)
|
|
619
|
+
|
|
620
|
+
# Check connection health
|
|
621
|
+
self._check_connections_health()
|
|
622
|
+
|
|
623
|
+
# Update last health check time
|
|
624
|
+
self.last_health_check = datetime.now(timezone.utc)
|
|
625
|
+
|
|
626
|
+
except Exception as e:
|
|
627
|
+
self.logger.error(f"Health monitor error: {e}")
|
|
628
|
+
time.sleep(5) # Brief pause on error
|
|
629
|
+
|
|
630
|
+
self.logger.debug("Health monitor stopped")
|
|
631
|
+
|
|
632
|
+
def _check_connections_health(self):
|
|
633
|
+
"""Check health of all connections in the pool."""
|
|
634
|
+
with self.pool_lock:
|
|
635
|
+
unhealthy_connections = []
|
|
636
|
+
|
|
637
|
+
# Check each connection's health
|
|
638
|
+
for conn_id, client in list(self.active_connections.items()):
|
|
639
|
+
stats = self.connection_stats.get(conn_id)
|
|
640
|
+
if not stats:
|
|
641
|
+
continue
|
|
642
|
+
|
|
643
|
+
# Health criteria:
|
|
644
|
+
# 1. Too many consecutive errors
|
|
645
|
+
if stats.consecutive_errors > 3:
|
|
646
|
+
unhealthy_connections.append((conn_id, client, "excessive_errors"))
|
|
647
|
+
continue
|
|
648
|
+
|
|
649
|
+
# 2. Connection is not actually connected
|
|
650
|
+
if not client.connected and stats.is_connected:
|
|
651
|
+
unhealthy_connections.append((conn_id, client, "disconnected"))
|
|
652
|
+
stats.is_connected = False
|
|
653
|
+
continue
|
|
654
|
+
|
|
655
|
+
# 3. Connection idle for too long (>5 minutes)
|
|
656
|
+
idle_time = (
|
|
657
|
+
datetime.now(timezone.utc) - stats.last_used
|
|
658
|
+
).total_seconds()
|
|
659
|
+
if idle_time > 300 and conn_id not in [
|
|
660
|
+
id for id, _ in enumerate(self.available_connections)
|
|
661
|
+
]:
|
|
662
|
+
unhealthy_connections.append((conn_id, client, "idle_timeout"))
|
|
663
|
+
continue
|
|
664
|
+
|
|
665
|
+
# 4. High error rate (>10% of events)
|
|
666
|
+
if stats.events_sent > 100 and stats.errors > stats.events_sent * 0.1:
|
|
667
|
+
unhealthy_connections.append((conn_id, client, "high_error_rate"))
|
|
668
|
+
|
|
669
|
+
# Remove unhealthy connections
|
|
670
|
+
for conn_id, client, reason in unhealthy_connections:
|
|
671
|
+
self.logger.warning(
|
|
672
|
+
f"Removing unhealthy connection {conn_id}: {reason}"
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
# Remove from active connections
|
|
676
|
+
self.active_connections.pop(conn_id, None)
|
|
677
|
+
|
|
678
|
+
# Remove from available if present
|
|
679
|
+
if client in self.available_connections:
|
|
680
|
+
self.available_connections.remove(client)
|
|
681
|
+
|
|
682
|
+
# Try to disconnect
|
|
683
|
+
try:
|
|
684
|
+
if client.connected:
|
|
685
|
+
threading.Thread(
|
|
686
|
+
target=lambda: asyncio.run(client.disconnect()), daemon=True
|
|
687
|
+
).start()
|
|
688
|
+
except Exception as e:
|
|
689
|
+
self.logger.debug(f"Error disconnecting unhealthy connection: {e}")
|
|
690
|
+
|
|
691
|
+
# Remove stats
|
|
692
|
+
self.connection_stats.pop(conn_id, None)
|
|
693
|
+
|
|
694
|
+
# Log health check results
|
|
695
|
+
if unhealthy_connections:
|
|
696
|
+
self.logger.info(
|
|
697
|
+
f"Health check removed {len(unhealthy_connections)} unhealthy connections"
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
# Pre-create connections if pool is too small
|
|
701
|
+
current_total = len(self.active_connections) + len(
|
|
702
|
+
self.available_connections
|
|
703
|
+
)
|
|
704
|
+
if current_total < min(2, self.max_connections):
|
|
705
|
+
self.logger.debug("Pre-creating connections to maintain pool minimum")
|
|
706
|
+
for _ in range(min(2, self.max_connections) - current_total):
|
|
707
|
+
client = self._create_client()
|
|
708
|
+
if client:
|
|
709
|
+
conn_id = (
|
|
710
|
+
f"pool_{len(self.active_connections)}_{int(time.time())}"
|
|
711
|
+
)
|
|
712
|
+
self.active_connections[conn_id] = client
|
|
713
|
+
self.available_connections.append(client)
|
|
714
|
+
|
|
715
|
+
async def _ping_connection(self, client: socketio.AsyncClient) -> bool:
|
|
716
|
+
"""Ping a connection to check if it's alive.
|
|
717
|
+
|
|
718
|
+
Args:
|
|
719
|
+
client: The Socket.IO client to ping
|
|
720
|
+
|
|
721
|
+
Returns:
|
|
722
|
+
True if connection is healthy, False otherwise
|
|
723
|
+
"""
|
|
724
|
+
try:
|
|
725
|
+
# Send a ping and wait for response
|
|
726
|
+
await asyncio.wait_for(
|
|
727
|
+
client.emit("ping", {"timestamp": time.time()}, namespace="/health"),
|
|
728
|
+
timeout=1.0,
|
|
729
|
+
)
|
|
730
|
+
return True
|
|
731
|
+
except (asyncio.TimeoutError, Exception):
|
|
732
|
+
return False
|
|
733
|
+
|
|
734
|
+
def emit(self, event: str, data: Dict[str, Any]) -> bool:
|
|
735
|
+
"""Emit an event through the connection pool.
|
|
736
|
+
|
|
737
|
+
This method provides compatibility for the legacy emit() interface.
|
|
738
|
+
For critical hook events, we use direct emission to avoid batching delays.
|
|
739
|
+
|
|
740
|
+
Args:
|
|
741
|
+
event: Event name (e.g., "claude_event")
|
|
742
|
+
data: Event data dictionary
|
|
743
|
+
|
|
744
|
+
Returns:
|
|
745
|
+
bool: True if event was sent successfully (always True for async emission)
|
|
746
|
+
"""
|
|
747
|
+
if not SOCKETIO_AVAILABLE or not self._running:
|
|
748
|
+
return False
|
|
749
|
+
|
|
750
|
+
# For critical claude_event, use direct emission to avoid batching delays
|
|
751
|
+
if event == "claude_event":
|
|
752
|
+
return self._emit_direct(event, data)
|
|
753
|
+
|
|
754
|
+
# Map to the modern emit_event method using default namespace
|
|
755
|
+
self.emit_event("/", event, data)
|
|
756
|
+
return True
|
|
757
|
+
|
|
758
|
+
def _emit_direct(self, event: str, data: Dict[str, Any]) -> bool:
|
|
759
|
+
"""Emit an event directly without batching.
|
|
760
|
+
|
|
761
|
+
This is used for critical events that need immediate delivery.
|
|
762
|
+
"""
|
|
763
|
+
try:
|
|
764
|
+
# Create a synchronous client for direct emission
|
|
765
|
+
import socketio
|
|
766
|
+
|
|
767
|
+
client = socketio.Client(logger=False, engineio_logger=False)
|
|
768
|
+
|
|
769
|
+
# Quick connect, emit, and disconnect
|
|
770
|
+
client.connect(self.server_url, wait=True, wait_timeout=1.0)
|
|
771
|
+
client.emit(event, data)
|
|
772
|
+
client.disconnect()
|
|
773
|
+
|
|
774
|
+
# Update stats
|
|
775
|
+
for stats in self.connection_stats.values():
|
|
776
|
+
stats.events_sent += 1
|
|
777
|
+
break
|
|
778
|
+
|
|
779
|
+
return True
|
|
780
|
+
except Exception as e:
|
|
781
|
+
self.logger.debug(f"Direct emit failed: {e}")
|
|
782
|
+
# Fall back to batched emission
|
|
783
|
+
self.emit_event("/", event, data)
|
|
784
|
+
return True
|
|
785
|
+
|
|
541
786
|
def get_stats(self) -> Dict[str, Any]:
|
|
542
787
|
"""Get connection pool statistics."""
|
|
543
788
|
with self.pool_lock:
|
|
789
|
+
# Calculate health metrics
|
|
790
|
+
healthy_connections = sum(
|
|
791
|
+
1
|
|
792
|
+
for stats in self.connection_stats.values()
|
|
793
|
+
if stats.is_connected and stats.consecutive_errors < 3
|
|
794
|
+
)
|
|
795
|
+
|
|
544
796
|
return {
|
|
545
797
|
"max_connections": self.max_connections,
|
|
546
798
|
"available_connections": len(self.available_connections),
|
|
547
799
|
"active_connections": len(self.active_connections),
|
|
548
|
-
"
|
|
549
|
-
"
|
|
800
|
+
"healthy_connections": healthy_connections,
|
|
801
|
+
"total_events_sent": sum(
|
|
802
|
+
stats.events_sent for stats in self.connection_stats.values()
|
|
803
|
+
),
|
|
804
|
+
"total_errors": sum(
|
|
805
|
+
stats.errors for stats in self.connection_stats.values()
|
|
806
|
+
),
|
|
550
807
|
"circuit_state": self.circuit_breaker.state.value,
|
|
551
808
|
"circuit_failures": self.circuit_breaker.failure_count,
|
|
552
809
|
"batch_queue_size": len(self.batch_queue),
|
|
553
|
-
"server_url": self.server_url
|
|
810
|
+
"server_url": self.server_url,
|
|
811
|
+
"last_health_check": (
|
|
812
|
+
self.last_health_check.isoformat()
|
|
813
|
+
if hasattr(self, "last_health_check")
|
|
814
|
+
else None
|
|
815
|
+
),
|
|
816
|
+
"health_check_interval": self.health_check_interval,
|
|
554
817
|
}
|
|
555
818
|
|
|
556
819
|
|
|
@@ -579,4 +842,4 @@ def stop_connection_pool():
|
|
|
579
842
|
def emit_hook_event(namespace: str, event: str, data: Dict[str, Any]):
|
|
580
843
|
"""Emit a hook event using the connection pool."""
|
|
581
844
|
pool = get_connection_pool()
|
|
582
|
-
pool.emit_event(namespace, event, data)
|
|
845
|
+
pool.emit_event(namespace, event, data)
|