claude-mpm 3.9.11__py3-none-any.whl → 4.0.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +1 -1
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +203 -81
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +305 -197
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +104 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  113. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  114. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  115. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  116. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  117. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  118. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  119. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  120. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  121. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  122. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  123. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  124. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  125. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  126. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  127. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  128. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  129. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  130. claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
  131. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  132. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
  133. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  134. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  135. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  136. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  137. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  138. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  139. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  140. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  141. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  142. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  143. claude_mpm/dashboard/static/js/socket-client.js +120 -54
  144. claude_mpm/dashboard/templates/index.html +40 -50
  145. claude_mpm/experimental/cli_enhancements.py +60 -58
  146. claude_mpm/generators/__init__.py +1 -1
  147. claude_mpm/generators/agent_profile_generator.py +75 -65
  148. claude_mpm/hooks/__init__.py +1 -1
  149. claude_mpm/hooks/base_hook.py +33 -28
  150. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  151. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  152. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  153. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  154. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  155. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  156. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  157. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  158. claude_mpm/hooks/memory_integration_hook.py +140 -100
  159. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  160. claude_mpm/hooks/validation_hooks.py +57 -49
  161. claude_mpm/init.py +145 -121
  162. claude_mpm/models/__init__.py +9 -9
  163. claude_mpm/models/agent_definition.py +33 -23
  164. claude_mpm/models/agent_session.py +228 -200
  165. claude_mpm/scripts/__init__.py +1 -1
  166. claude_mpm/scripts/socketio_daemon.py +192 -75
  167. claude_mpm/scripts/socketio_server_manager.py +328 -0
  168. claude_mpm/scripts/start_activity_logging.py +25 -22
  169. claude_mpm/services/__init__.py +68 -43
  170. claude_mpm/services/agent_capabilities_service.py +271 -0
  171. claude_mpm/services/agents/__init__.py +23 -32
  172. claude_mpm/services/agents/deployment/__init__.py +3 -3
  173. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  174. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  175. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  176. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  177. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  178. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  179. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  180. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  181. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  182. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  183. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  184. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  185. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  186. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  187. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  188. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  189. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  190. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  191. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  192. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  193. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  194. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  195. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  196. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  197. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  198. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  199. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  200. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  201. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  202. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  203. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  204. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  205. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  206. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  207. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  208. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  209. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  210. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  211. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  212. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  213. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  214. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  215. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  216. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  217. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  218. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  219. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  220. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  221. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  222. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  223. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  224. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  225. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  226. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  227. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  228. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  229. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  230. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  231. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  232. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  233. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  234. claude_mpm/services/agents/loading/__init__.py +2 -2
  235. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  236. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  237. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  238. claude_mpm/services/agents/management/__init__.py +2 -2
  239. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  240. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  241. claude_mpm/services/agents/memory/__init__.py +9 -6
  242. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  243. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  244. claude_mpm/services/agents/memory/analyzer.py +430 -0
  245. claude_mpm/services/agents/memory/content_manager.py +376 -0
  246. claude_mpm/services/agents/memory/template_generator.py +468 -0
  247. claude_mpm/services/agents/registry/__init__.py +7 -10
  248. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  249. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  250. claude_mpm/services/async_session_logger.py +187 -153
  251. claude_mpm/services/claude_session_logger.py +87 -72
  252. claude_mpm/services/command_handler_service.py +217 -0
  253. claude_mpm/services/communication/__init__.py +3 -2
  254. claude_mpm/services/core/__init__.py +50 -97
  255. claude_mpm/services/core/base.py +60 -53
  256. claude_mpm/services/core/interfaces/__init__.py +188 -0
  257. claude_mpm/services/core/interfaces/agent.py +351 -0
  258. claude_mpm/services/core/interfaces/communication.py +343 -0
  259. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  260. claude_mpm/services/core/interfaces/service.py +434 -0
  261. claude_mpm/services/core/interfaces.py +19 -944
  262. claude_mpm/services/event_aggregator.py +208 -170
  263. claude_mpm/services/exceptions.py +387 -308
  264. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  265. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  266. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  267. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  268. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  269. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  270. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  271. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  272. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  273. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  274. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  275. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  276. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  277. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  278. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  279. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  280. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  281. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  283. claude_mpm/services/hook_service.py +106 -114
  284. claude_mpm/services/infrastructure/__init__.py +7 -5
  285. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  286. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  287. claude_mpm/services/infrastructure/logging.py +83 -76
  288. claude_mpm/services/infrastructure/monitoring.py +547 -404
  289. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  290. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  291. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  292. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  293. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  294. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  295. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  296. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  297. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  298. claude_mpm/services/mcp_gateway/main.py +287 -137
  299. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  302. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  309. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  310. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  311. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  312. claude_mpm/services/memory/__init__.py +2 -2
  313. claude_mpm/services/memory/builder.py +451 -362
  314. claude_mpm/services/memory/cache/__init__.py +2 -2
  315. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  316. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  317. claude_mpm/services/memory/indexed_memory.py +195 -193
  318. claude_mpm/services/memory/optimizer.py +267 -234
  319. claude_mpm/services/memory/router.py +571 -263
  320. claude_mpm/services/memory_hook_service.py +237 -0
  321. claude_mpm/services/port_manager.py +223 -0
  322. claude_mpm/services/project/__init__.py +3 -3
  323. claude_mpm/services/project/analyzer.py +451 -305
  324. claude_mpm/services/project/registry.py +262 -240
  325. claude_mpm/services/recovery_manager.py +287 -231
  326. claude_mpm/services/response_tracker.py +87 -67
  327. claude_mpm/services/runner_configuration_service.py +587 -0
  328. claude_mpm/services/session_management_service.py +304 -0
  329. claude_mpm/services/socketio/__init__.py +4 -4
  330. claude_mpm/services/socketio/client_proxy.py +174 -0
  331. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  332. claude_mpm/services/socketio/handlers/base.py +44 -30
  333. claude_mpm/services/socketio/handlers/connection.py +145 -65
  334. claude_mpm/services/socketio/handlers/file.py +123 -108
  335. claude_mpm/services/socketio/handlers/git.py +607 -373
  336. claude_mpm/services/socketio/handlers/hook.py +170 -0
  337. claude_mpm/services/socketio/handlers/memory.py +4 -4
  338. claude_mpm/services/socketio/handlers/project.py +4 -4
  339. claude_mpm/services/socketio/handlers/registry.py +53 -38
  340. claude_mpm/services/socketio/server/__init__.py +18 -0
  341. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  342. claude_mpm/services/socketio/server/core.py +399 -0
  343. claude_mpm/services/socketio/server/main.py +323 -0
  344. claude_mpm/services/socketio_client_manager.py +160 -133
  345. claude_mpm/services/socketio_server.py +36 -1885
  346. claude_mpm/services/subprocess_launcher_service.py +316 -0
  347. claude_mpm/services/system_instructions_service.py +258 -0
  348. claude_mpm/services/ticket_manager.py +19 -533
  349. claude_mpm/services/utility_service.py +285 -0
  350. claude_mpm/services/version_control/__init__.py +18 -21
  351. claude_mpm/services/version_control/branch_strategy.py +20 -10
  352. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  353. claude_mpm/services/version_control/git_operations.py +52 -21
  354. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  355. claude_mpm/services/version_control/version_parser.py +145 -125
  356. claude_mpm/services/version_service.py +270 -0
  357. claude_mpm/storage/__init__.py +2 -2
  358. claude_mpm/storage/state_storage.py +177 -181
  359. claude_mpm/ticket_wrapper.py +2 -2
  360. claude_mpm/utils/__init__.py +2 -2
  361. claude_mpm/utils/agent_dependency_loader.py +453 -243
  362. claude_mpm/utils/config_manager.py +157 -118
  363. claude_mpm/utils/console.py +1 -1
  364. claude_mpm/utils/dependency_cache.py +102 -107
  365. claude_mpm/utils/dependency_manager.py +52 -47
  366. claude_mpm/utils/dependency_strategies.py +131 -96
  367. claude_mpm/utils/environment_context.py +110 -102
  368. claude_mpm/utils/error_handler.py +75 -55
  369. claude_mpm/utils/file_utils.py +80 -67
  370. claude_mpm/utils/framework_detection.py +12 -11
  371. claude_mpm/utils/import_migration_example.py +12 -60
  372. claude_mpm/utils/imports.py +48 -45
  373. claude_mpm/utils/path_operations.py +100 -93
  374. claude_mpm/utils/robust_installer.py +172 -164
  375. claude_mpm/utils/session_logging.py +30 -23
  376. claude_mpm/utils/subprocess_utils.py +99 -61
  377. claude_mpm/validation/__init__.py +1 -1
  378. claude_mpm/validation/agent_validator.py +151 -111
  379. claude_mpm/validation/frontmatter_validator.py +92 -71
  380. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
  381. claude_mpm-4.0.3.dist-info/RECORD +402 -0
  382. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
  383. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
  384. claude_mpm/cli/commands/run_guarded.py +0 -511
  385. claude_mpm/config/memory_guardian_config.py +0 -325
  386. claude_mpm/config/memory_guardian_yaml.py +0 -335
  387. claude_mpm/core/config_paths.py +0 -150
  388. claude_mpm/core/memory_aware_runner.py +0 -353
  389. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  390. claude_mpm/deployment_paths.py +0 -261
  391. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  392. claude_mpm/models/state_models.py +0 -433
  393. claude_mpm/services/agent/__init__.py +0 -24
  394. claude_mpm/services/agent/deployment.py +0 -2548
  395. claude_mpm/services/agent/management.py +0 -598
  396. claude_mpm/services/agent/registry.py +0 -813
  397. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  398. claude_mpm/services/communication/socketio.py +0 -1935
  399. claude_mpm/services/communication/websocket.py +0 -479
  400. claude_mpm/services/framework_claude_md_generator.py +0 -624
  401. claude_mpm/services/health_monitor.py +0 -893
  402. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  403. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  404. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  405. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  406. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  407. claude_mpm/services/infrastructure/state_manager.py +0 -774
  408. claude_mpm/services/mcp_gateway/manager.py +0 -334
  409. claude_mpm/services/optimized_hook_service.py +0 -542
  410. claude_mpm/services/project_analyzer.py +0 -864
  411. claude_mpm/services/project_registry.py +0 -608
  412. claude_mpm/services/standalone_socketio_server.py +0 -1300
  413. claude_mpm/services/ticket_manager_di.py +0 -318
  414. claude_mpm/services/ticketing_service_original.py +0 -510
  415. claude_mpm/utils/paths.py +0 -395
  416. claude_mpm/utils/platform_memory.py +0 -524
  417. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  418. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
  419. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
@@ -1,642 +0,0 @@
1
- """Restart loop protection service for Memory Guardian.
2
-
3
- Implements advanced restart loop detection and prevention with exponential backoff,
4
- circuit breaker patterns, and memory leak detection through trend analysis.
5
- """
6
-
7
- import asyncio
8
- import json
9
- import logging
10
- import time
11
- from dataclasses import dataclass, field
12
- from datetime import datetime, timedelta
13
- from enum import Enum
14
- from pathlib import Path
15
- from typing import Dict, List, Optional, Tuple, Any
16
- from collections import deque
17
-
18
- from claude_mpm.services.core.base import BaseService
19
-
20
-
21
- class CircuitState(Enum):
22
- """Circuit breaker states."""
23
- CLOSED = "closed" # Normal operation
24
- OPEN = "open" # Failures exceeded, blocking restarts
25
- HALF_OPEN = "half_open" # Testing recovery
26
-
27
-
28
- @dataclass
29
- class RestartRecord:
30
- """Record of a restart event."""
31
- timestamp: float
32
- reason: str
33
- memory_mb: float
34
- success: bool
35
- backoff_seconds: float = 0.0
36
-
37
- def to_dict(self) -> Dict[str, Any]:
38
- """Convert to dictionary."""
39
- return {
40
- 'timestamp': self.timestamp,
41
- 'timestamp_iso': datetime.fromtimestamp(self.timestamp).isoformat(),
42
- 'reason': self.reason,
43
- 'memory_mb': self.memory_mb,
44
- 'success': self.success,
45
- 'backoff_seconds': self.backoff_seconds
46
- }
47
-
48
-
49
- @dataclass
50
- class MemoryTrend:
51
- """Memory usage trend analysis."""
52
- slope: float # MB per minute
53
- intercept: float
54
- r_squared: float # Correlation coefficient
55
- samples: int
56
- timespan_minutes: float
57
-
58
- @property
59
- def is_leak_suspected(self) -> bool:
60
- """Check if memory leak is suspected based on trend."""
61
- # Suspect leak if:
62
- # 1. Strong positive correlation (r² > 0.7)
63
- # 2. Growth rate > 10 MB/minute
64
- # 3. At least 10 samples over 5 minutes
65
- return (
66
- self.r_squared > 0.7 and
67
- self.slope > 10.0 and
68
- self.samples >= 10 and
69
- self.timespan_minutes >= 5
70
- )
71
-
72
- def to_dict(self) -> Dict[str, Any]:
73
- """Convert to dictionary."""
74
- return {
75
- 'slope_mb_per_min': round(self.slope, 2),
76
- 'intercept_mb': round(self.intercept, 2),
77
- 'r_squared': round(self.r_squared, 4),
78
- 'samples': self.samples,
79
- 'timespan_minutes': round(self.timespan_minutes, 2),
80
- 'leak_suspected': self.is_leak_suspected
81
- }
82
-
83
-
84
- @dataclass
85
- class RestartStatistics:
86
- """Restart statistics and diagnostics."""
87
- total_restarts: int = 0
88
- successful_restarts: int = 0
89
- failed_restarts: int = 0
90
- consecutive_failures: int = 0
91
- last_restart_time: Optional[float] = None
92
- average_interval_minutes: float = 0.0
93
- shortest_interval_minutes: float = float('inf')
94
- memory_trend: Optional[MemoryTrend] = None
95
- circuit_state: CircuitState = CircuitState.CLOSED
96
- circuit_trips: int = 0
97
-
98
- def to_dict(self) -> Dict[str, Any]:
99
- """Convert to dictionary."""
100
- return {
101
- 'total_restarts': self.total_restarts,
102
- 'successful_restarts': self.successful_restarts,
103
- 'failed_restarts': self.failed_restarts,
104
- 'success_rate': (self.successful_restarts / self.total_restarts * 100) if self.total_restarts > 0 else 0,
105
- 'consecutive_failures': self.consecutive_failures,
106
- 'last_restart_time': self.last_restart_time,
107
- 'last_restart_iso': datetime.fromtimestamp(self.last_restart_time).isoformat() if self.last_restart_time else None,
108
- 'average_interval_minutes': round(self.average_interval_minutes, 2),
109
- 'shortest_interval_minutes': round(self.shortest_interval_minutes, 2) if self.shortest_interval_minutes != float('inf') else None,
110
- 'memory_trend': self.memory_trend.to_dict() if self.memory_trend else None,
111
- 'circuit_state': self.circuit_state.value,
112
- 'circuit_trips': self.circuit_trips
113
- }
114
-
115
-
116
- class RestartProtection(BaseService):
117
- """Service for preventing restart loops and detecting memory leaks."""
118
-
119
- def __init__(
120
- self,
121
- max_restarts_per_hour: int = 5,
122
- max_consecutive_failures: int = 3,
123
- base_backoff_seconds: int = 1,
124
- max_backoff_seconds: int = 300,
125
- circuit_reset_minutes: int = 30,
126
- memory_sample_window_minutes: int = 10,
127
- state_file: Optional[Path] = None
128
- ):
129
- """Initialize restart protection service.
130
-
131
- Args:
132
- max_restarts_per_hour: Maximum restarts allowed per hour
133
- max_consecutive_failures: Failures before circuit breaker opens
134
- base_backoff_seconds: Base exponential backoff time
135
- max_backoff_seconds: Maximum backoff time
136
- circuit_reset_minutes: Time before circuit breaker resets
137
- memory_sample_window_minutes: Window for memory trend analysis
138
- state_file: Optional file for persisting state
139
- """
140
- super().__init__("RestartProtection")
141
-
142
- # Configuration
143
- self.max_restarts_per_hour = max_restarts_per_hour
144
- self.max_consecutive_failures = max_consecutive_failures
145
- self.base_backoff_seconds = base_backoff_seconds
146
- self.max_backoff_seconds = max_backoff_seconds
147
- self.circuit_reset_minutes = circuit_reset_minutes
148
- self.memory_sample_window_minutes = memory_sample_window_minutes
149
- self.state_file = state_file
150
-
151
- # State tracking
152
- self.restart_history: deque = deque(maxlen=100) # Last 100 restarts
153
- self.memory_samples: deque = deque(maxlen=1000) # Last 1000 memory samples
154
- self.statistics = RestartStatistics()
155
-
156
- # Circuit breaker state
157
- self.circuit_state = CircuitState.CLOSED
158
- self.circuit_opened_at: Optional[float] = None
159
- self.circuit_test_allowed_at: Optional[float] = None
160
-
161
- # Load persisted state if available
162
- if self.state_file:
163
- self._load_state()
164
-
165
- self.log_info(
166
- f"Restart protection initialized: "
167
- f"max_restarts={max_restarts_per_hour}/hour, "
168
- f"max_failures={max_consecutive_failures}, "
169
- f"backoff={base_backoff_seconds}-{max_backoff_seconds}s"
170
- )
171
-
172
- async def initialize(self) -> bool:
173
- """Initialize the restart protection service.
174
-
175
- Returns:
176
- True if initialization successful
177
- """
178
- try:
179
- self.log_info("Initializing restart protection service")
180
-
181
- # Start background analysis task
182
- asyncio.create_task(self._periodic_analysis())
183
-
184
- self._initialized = True
185
- self.log_info("Restart protection service initialized successfully")
186
- return True
187
-
188
- except Exception as e:
189
- self.log_error(f"Failed to initialize restart protection: {e}")
190
- return False
191
-
192
- async def shutdown(self) -> None:
193
- """Shutdown the restart protection service."""
194
- try:
195
- self.log_info("Shutting down restart protection service")
196
-
197
- # Save state if configured
198
- if self.state_file:
199
- self._save_state()
200
-
201
- self._shutdown = True
202
- self.log_info("Restart protection service shutdown complete")
203
-
204
- except Exception as e:
205
- self.log_error(f"Error during restart protection shutdown: {e}")
206
-
207
- def should_allow_restart(self, current_memory_mb: float = 0) -> Tuple[bool, str]:
208
- """Check if restart should be allowed based on protection rules.
209
-
210
- Args:
211
- current_memory_mb: Current memory usage in MB
212
-
213
- Returns:
214
- Tuple of (allowed, reason)
215
- """
216
- # Check circuit breaker state
217
- circuit_check = self._check_circuit_breaker()
218
- if not circuit_check[0]:
219
- return circuit_check
220
-
221
- # Check restart frequency
222
- frequency_check = self._check_restart_frequency()
223
- if not frequency_check[0]:
224
- return frequency_check
225
-
226
- # Check for memory leak pattern
227
- if current_memory_mb > 0:
228
- leak_check = self._check_memory_leak_pattern(current_memory_mb)
229
- if not leak_check[0]:
230
- return leak_check
231
-
232
- # Check consecutive failures
233
- if self.statistics.consecutive_failures >= self.max_consecutive_failures:
234
- return False, f"Too many consecutive failures ({self.statistics.consecutive_failures})"
235
-
236
- return True, "Restart allowed"
237
-
238
- def record_restart(
239
- self,
240
- reason: str,
241
- memory_mb: float,
242
- success: bool,
243
- backoff_applied: float = 0
244
- ) -> None:
245
- """Record a restart event.
246
-
247
- Args:
248
- reason: Reason for restart
249
- memory_mb: Memory usage at restart
250
- success: Whether restart was successful
251
- backoff_applied: Backoff time applied (seconds)
252
- """
253
- record = RestartRecord(
254
- timestamp=time.time(),
255
- reason=reason,
256
- memory_mb=memory_mb,
257
- success=success,
258
- backoff_seconds=backoff_applied
259
- )
260
-
261
- self.restart_history.append(record)
262
-
263
- # Update statistics
264
- self.statistics.total_restarts += 1
265
- if success:
266
- self.statistics.successful_restarts += 1
267
- self.statistics.consecutive_failures = 0
268
-
269
- # Update circuit breaker state if in half-open
270
- if self.circuit_state == CircuitState.HALF_OPEN:
271
- self._close_circuit_breaker()
272
- else:
273
- self.statistics.failed_restarts += 1
274
- self.statistics.consecutive_failures += 1
275
-
276
- # Check if circuit breaker should trip
277
- if self.statistics.consecutive_failures >= self.max_consecutive_failures:
278
- self._open_circuit_breaker()
279
-
280
- self.statistics.last_restart_time = record.timestamp
281
-
282
- # Update interval statistics
283
- self._update_interval_statistics()
284
-
285
- # Add memory sample
286
- self.record_memory_sample(memory_mb)
287
-
288
- self.log_info(
289
- f"Restart recorded: {reason}, "
290
- f"memory={memory_mb:.2f}MB, "
291
- f"success={success}, "
292
- f"consecutive_failures={self.statistics.consecutive_failures}"
293
- )
294
-
295
- def record_memory_sample(self, memory_mb: float) -> None:
296
- """Record a memory usage sample for trend analysis.
297
-
298
- Args:
299
- memory_mb: Memory usage in MB
300
- """
301
- self.memory_samples.append({
302
- 'timestamp': time.time(),
303
- 'memory_mb': memory_mb
304
- })
305
-
306
- def detect_memory_leak(self) -> Optional[MemoryTrend]:
307
- """Analyze memory trends to detect potential leaks.
308
-
309
- Returns:
310
- MemoryTrend if analysis possible, None otherwise
311
- """
312
- if len(self.memory_samples) < 10:
313
- return None
314
-
315
- # Get samples within analysis window
316
- cutoff_time = time.time() - (self.memory_sample_window_minutes * 60)
317
- recent_samples = [
318
- s for s in self.memory_samples
319
- if s['timestamp'] >= cutoff_time
320
- ]
321
-
322
- if len(recent_samples) < 10:
323
- return None
324
-
325
- # Perform linear regression to detect trend
326
- trend = self._calculate_memory_trend(recent_samples)
327
-
328
- # Update statistics
329
- self.statistics.memory_trend = trend
330
-
331
- if trend.is_leak_suspected:
332
- self.log_warning(
333
- f"Memory leak suspected: "
334
- f"growth rate={trend.slope:.2f}MB/min, "
335
- f"correlation={trend.r_squared:.3f}"
336
- )
337
-
338
- return trend
339
-
340
- def get_restart_statistics(self) -> RestartStatistics:
341
- """Get comprehensive restart statistics.
342
-
343
- Returns:
344
- RestartStatistics object
345
- """
346
- # Update memory trend
347
- self.detect_memory_leak()
348
-
349
- return self.statistics
350
-
351
- def get_backoff_seconds(self, attempt: int) -> float:
352
- """Calculate exponential backoff time for restart attempt.
353
-
354
- Args:
355
- attempt: Attempt number (1-based)
356
-
357
- Returns:
358
- Backoff time in seconds
359
- """
360
- # Exponential backoff: base * 2^(attempt-1)
361
- backoff = self.base_backoff_seconds * (2 ** (attempt - 1))
362
-
363
- # Apply maximum limit
364
- backoff = min(backoff, self.max_backoff_seconds)
365
-
366
- # Add jitter (±10%) to prevent thundering herd
367
- import random
368
- jitter = backoff * 0.1 * (2 * random.random() - 1)
369
- backoff += jitter
370
-
371
- return max(0, backoff)
372
-
373
- def reset_circuit_breaker(self) -> bool:
374
- """Manually reset the circuit breaker.
375
-
376
- Returns:
377
- True if reset successful
378
- """
379
- if self.circuit_state == CircuitState.CLOSED:
380
- self.log_info("Circuit breaker is already closed")
381
- return True
382
-
383
- self.log_info("Manually resetting circuit breaker")
384
- self._close_circuit_breaker()
385
-
386
- # Reset consecutive failures
387
- self.statistics.consecutive_failures = 0
388
-
389
- return True
390
-
391
- def _check_circuit_breaker(self) -> Tuple[bool, str]:
392
- """Check circuit breaker state.
393
-
394
- Returns:
395
- Tuple of (allowed, reason)
396
- """
397
- if self.circuit_state == CircuitState.CLOSED:
398
- return True, "Circuit closed"
399
-
400
- if self.circuit_state == CircuitState.OPEN:
401
- # Check if enough time has passed to test
402
- if self.circuit_test_allowed_at and time.time() >= self.circuit_test_allowed_at:
403
- self._half_open_circuit_breaker()
404
- return True, "Circuit half-open, testing recovery"
405
- else:
406
- remaining = self.circuit_test_allowed_at - time.time() if self.circuit_test_allowed_at else 0
407
- return False, f"Circuit breaker open, reset in {remaining:.0f}s"
408
-
409
- if self.circuit_state == CircuitState.HALF_OPEN:
410
- return True, "Circuit half-open, testing recovery"
411
-
412
- return False, "Unknown circuit state"
413
-
414
- def _check_restart_frequency(self) -> Tuple[bool, str]:
415
- """Check if restart frequency is within limits.
416
-
417
- Returns:
418
- Tuple of (allowed, reason)
419
- """
420
- # Count restarts in last hour
421
- hour_ago = time.time() - 3600
422
- recent_restarts = [
423
- r for r in self.restart_history
424
- if r.timestamp >= hour_ago
425
- ]
426
-
427
- if len(recent_restarts) >= self.max_restarts_per_hour:
428
- return False, f"Too many restarts ({len(recent_restarts)}/{self.max_restarts_per_hour} per hour)"
429
-
430
- return True, "Frequency within limits"
431
-
432
- def _check_memory_leak_pattern(self, current_memory_mb: float) -> Tuple[bool, str]:
433
- """Check for memory leak pattern.
434
-
435
- Args:
436
- current_memory_mb: Current memory usage
437
-
438
- Returns:
439
- Tuple of (allowed, reason)
440
- """
441
- trend = self.detect_memory_leak()
442
-
443
- if trend and trend.is_leak_suspected:
444
- # Check if memory is still growing
445
- predicted_memory = trend.slope * (time.time() / 60) + trend.intercept
446
- if current_memory_mb > predicted_memory * 1.1: # 10% above prediction
447
- return False, f"Memory leak detected (growth: {trend.slope:.2f}MB/min)"
448
-
449
- return True, "No memory leak detected"
450
-
451
- def _open_circuit_breaker(self) -> None:
452
- """Open the circuit breaker."""
453
- self.circuit_state = CircuitState.OPEN
454
- self.circuit_opened_at = time.time()
455
- self.circuit_test_allowed_at = time.time() + (self.circuit_reset_minutes * 60)
456
- self.statistics.circuit_state = CircuitState.OPEN
457
- self.statistics.circuit_trips += 1
458
-
459
- self.log_warning(
460
- f"Circuit breaker opened after {self.statistics.consecutive_failures} failures, "
461
- f"will test recovery in {self.circuit_reset_minutes} minutes"
462
- )
463
-
464
- def _half_open_circuit_breaker(self) -> None:
465
- """Move circuit breaker to half-open state."""
466
- self.circuit_state = CircuitState.HALF_OPEN
467
- self.statistics.circuit_state = CircuitState.HALF_OPEN
468
-
469
- self.log_info("Circuit breaker moved to half-open state for testing")
470
-
471
- def _close_circuit_breaker(self) -> None:
472
- """Close the circuit breaker."""
473
- self.circuit_state = CircuitState.CLOSED
474
- self.circuit_opened_at = None
475
- self.circuit_test_allowed_at = None
476
- self.statistics.circuit_state = CircuitState.CLOSED
477
-
478
- self.log_info("Circuit breaker closed, normal operation resumed")
479
-
480
- def _update_interval_statistics(self) -> None:
481
- """Update restart interval statistics."""
482
- if len(self.restart_history) < 2:
483
- return
484
-
485
- # Calculate intervals between restarts
486
- intervals = []
487
- for i in range(1, len(self.restart_history)):
488
- interval = (self.restart_history[i].timestamp - self.restart_history[i-1].timestamp) / 60
489
- intervals.append(interval)
490
-
491
- if intervals:
492
- self.statistics.average_interval_minutes = sum(intervals) / len(intervals)
493
- self.statistics.shortest_interval_minutes = min(intervals)
494
-
495
- def _calculate_memory_trend(self, samples: List[Dict[str, float]]) -> MemoryTrend:
496
- """Calculate memory usage trend using linear regression.
497
-
498
- Args:
499
- samples: List of memory samples
500
-
501
- Returns:
502
- MemoryTrend object
503
- """
504
- if not samples:
505
- return MemoryTrend(0, 0, 0, 0, 0)
506
-
507
- # Convert timestamps to minutes from first sample
508
- base_time = samples[0]['timestamp']
509
- x_values = [(s['timestamp'] - base_time) / 60 for s in samples]
510
- y_values = [s['memory_mb'] for s in samples]
511
-
512
- # Calculate linear regression
513
- n = len(samples)
514
- if n < 2:
515
- return MemoryTrend(0, y_values[0], 0, n, 0)
516
-
517
- sum_x = sum(x_values)
518
- sum_y = sum(y_values)
519
- sum_xx = sum(x * x for x in x_values)
520
- sum_xy = sum(x * y for x, y in zip(x_values, y_values))
521
- sum_yy = sum(y * y for y in y_values)
522
-
523
- # Calculate slope and intercept
524
- denominator = n * sum_xx - sum_x * sum_x
525
- if abs(denominator) < 1e-10:
526
- # All x values are the same
527
- return MemoryTrend(0, sum_y / n, 0, n, 0)
528
-
529
- slope = (n * sum_xy - sum_x * sum_y) / denominator
530
- intercept = (sum_y - slope * sum_x) / n
531
-
532
- # Calculate R-squared
533
- y_mean = sum_y / n
534
- ss_tot = sum((y - y_mean) ** 2 for y in y_values)
535
-
536
- if ss_tot < 1e-10:
537
- # No variance in y values
538
- r_squared = 1.0
539
- else:
540
- ss_res = sum((y - (slope * x + intercept)) ** 2 for x, y in zip(x_values, y_values))
541
- r_squared = 1 - (ss_res / ss_tot)
542
-
543
- timespan = x_values[-1] - x_values[0] if len(x_values) > 1 else 0
544
-
545
- return MemoryTrend(
546
- slope=slope,
547
- intercept=intercept,
548
- r_squared=max(0, min(1, r_squared)),
549
- samples=n,
550
- timespan_minutes=timespan
551
- )
552
-
553
- async def _periodic_analysis(self) -> None:
554
- """Periodic background analysis task."""
555
- while not self._shutdown:
556
- try:
557
- # Detect memory leaks periodically
558
- self.detect_memory_leak()
559
-
560
- # Check circuit breaker timeout
561
- if self.circuit_state == CircuitState.OPEN:
562
- if self.circuit_test_allowed_at and time.time() >= self.circuit_test_allowed_at:
563
- self._half_open_circuit_breaker()
564
-
565
- # Save state periodically
566
- if self.state_file:
567
- self._save_state()
568
-
569
- await asyncio.sleep(60) # Run every minute
570
-
571
- except Exception as e:
572
- self.log_error(f"Error in periodic analysis: {e}")
573
- await asyncio.sleep(60)
574
-
575
- def _save_state(self) -> None:
576
- """Save service state to file."""
577
- if not self.state_file:
578
- return
579
-
580
- try:
581
- state = {
582
- 'restart_history': [r.to_dict() for r in self.restart_history],
583
- 'memory_samples': list(self.memory_samples)[-100:], # Save last 100
584
- 'statistics': self.statistics.to_dict(),
585
- 'circuit_state': self.circuit_state.value,
586
- 'circuit_opened_at': self.circuit_opened_at,
587
- 'circuit_test_allowed_at': self.circuit_test_allowed_at
588
- }
589
-
590
- self.state_file.parent.mkdir(parents=True, exist_ok=True)
591
- with open(self.state_file, 'w') as f:
592
- json.dump(state, f, indent=2)
593
-
594
- self.log_debug(f"Saved restart protection state to {self.state_file}")
595
-
596
- except Exception as e:
597
- self.log_error(f"Failed to save state: {e}")
598
-
599
- def _load_state(self) -> None:
600
- """Load service state from file."""
601
- if not self.state_file or not self.state_file.exists():
602
- return
603
-
604
- try:
605
- with open(self.state_file, 'r') as f:
606
- state = json.load(f)
607
-
608
- # Restore restart history
609
- for record_data in state.get('restart_history', []):
610
- record = RestartRecord(
611
- timestamp=record_data['timestamp'],
612
- reason=record_data['reason'],
613
- memory_mb=record_data['memory_mb'],
614
- success=record_data['success'],
615
- backoff_seconds=record_data.get('backoff_seconds', 0)
616
- )
617
- self.restart_history.append(record)
618
-
619
- # Restore memory samples
620
- for sample in state.get('memory_samples', []):
621
- self.memory_samples.append(sample)
622
-
623
- # Restore statistics
624
- stats_data = state.get('statistics', {})
625
- self.statistics.total_restarts = stats_data.get('total_restarts', 0)
626
- self.statistics.successful_restarts = stats_data.get('successful_restarts', 0)
627
- self.statistics.failed_restarts = stats_data.get('failed_restarts', 0)
628
- self.statistics.consecutive_failures = stats_data.get('consecutive_failures', 0)
629
- self.statistics.last_restart_time = stats_data.get('last_restart_time')
630
- self.statistics.circuit_trips = stats_data.get('circuit_trips', 0)
631
-
632
- # Restore circuit breaker state
633
- circuit_state_value = state.get('circuit_state', 'closed')
634
- self.circuit_state = CircuitState(circuit_state_value)
635
- self.statistics.circuit_state = self.circuit_state
636
- self.circuit_opened_at = state.get('circuit_opened_at')
637
- self.circuit_test_allowed_at = state.get('circuit_test_allowed_at')
638
-
639
- self.log_info(f"Loaded restart protection state from {self.state_file}")
640
-
641
- except Exception as e:
642
- self.log_error(f"Failed to load state: {e}")