claude-mpm 3.9.11__py3-none-any.whl → 4.0.4__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 (434) 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 +2 -2
  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 +330 -86
  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 +363 -220
  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 +124 -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/built/components/agent-inference.js +2 -0
  113. claude_mpm/dashboard/static/built/components/event-processor.js +2 -0
  114. claude_mpm/dashboard/static/built/components/event-viewer.js +2 -0
  115. claude_mpm/dashboard/static/built/components/export-manager.js +2 -0
  116. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +2 -0
  117. claude_mpm/dashboard/static/built/components/hud-library-loader.js +2 -0
  118. claude_mpm/dashboard/static/built/components/hud-manager.js +2 -0
  119. claude_mpm/dashboard/static/built/components/hud-visualizer.js +2 -0
  120. claude_mpm/dashboard/static/built/components/module-viewer.js +2 -0
  121. claude_mpm/dashboard/static/built/components/session-manager.js +2 -0
  122. claude_mpm/dashboard/static/built/components/socket-manager.js +2 -0
  123. claude_mpm/dashboard/static/built/components/ui-state-manager.js +2 -0
  124. claude_mpm/dashboard/static/built/components/working-directory.js +2 -0
  125. claude_mpm/dashboard/static/built/dashboard.js +2 -0
  126. claude_mpm/dashboard/static/built/socket-client.js +2 -0
  127. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  128. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  129. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  130. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  131. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  132. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  133. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  134. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  135. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  136. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  137. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  138. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  139. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  140. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  141. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  142. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  143. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  144. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  145. claude_mpm/dashboard/static/js/components/event-viewer.js +93 -72
  146. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  147. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +110 -96
  148. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  149. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  150. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  151. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  152. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  153. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  154. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  155. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  156. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  157. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  158. claude_mpm/dashboard/static/js/socket-client.js +133 -53
  159. claude_mpm/dashboard/templates/index.html +40 -50
  160. claude_mpm/experimental/cli_enhancements.py +60 -58
  161. claude_mpm/generators/__init__.py +1 -1
  162. claude_mpm/generators/agent_profile_generator.py +75 -65
  163. claude_mpm/hooks/__init__.py +1 -1
  164. claude_mpm/hooks/base_hook.py +33 -28
  165. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  166. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  167. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  168. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  169. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  170. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  171. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  172. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  173. claude_mpm/hooks/memory_integration_hook.py +140 -100
  174. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  175. claude_mpm/hooks/validation_hooks.py +57 -49
  176. claude_mpm/init.py +145 -121
  177. claude_mpm/models/__init__.py +9 -9
  178. claude_mpm/models/agent_definition.py +33 -23
  179. claude_mpm/models/agent_session.py +228 -200
  180. claude_mpm/scripts/__init__.py +1 -1
  181. claude_mpm/scripts/socketio_daemon.py +192 -75
  182. claude_mpm/scripts/socketio_server_manager.py +328 -0
  183. claude_mpm/scripts/start_activity_logging.py +25 -22
  184. claude_mpm/services/__init__.py +68 -43
  185. claude_mpm/services/agent_capabilities_service.py +271 -0
  186. claude_mpm/services/agents/__init__.py +23 -32
  187. claude_mpm/services/agents/deployment/__init__.py +3 -3
  188. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  189. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  190. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  191. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  192. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  193. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  194. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  195. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  196. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  197. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  198. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  199. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  200. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  201. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  202. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  203. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  204. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  205. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  206. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  207. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  208. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  209. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  210. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  211. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  212. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  213. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  214. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  215. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  216. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  217. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  218. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  219. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  220. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  221. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  222. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  223. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  224. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  225. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  226. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  227. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  228. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  229. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  230. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  231. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  232. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  233. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  234. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  235. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  236. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  237. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  238. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  239. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  240. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  241. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  242. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  243. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  244. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  245. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  246. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  247. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  248. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  249. claude_mpm/services/agents/loading/__init__.py +2 -2
  250. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  251. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  252. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  253. claude_mpm/services/agents/management/__init__.py +2 -2
  254. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  255. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  256. claude_mpm/services/agents/memory/__init__.py +9 -6
  257. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  258. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  259. claude_mpm/services/agents/memory/analyzer.py +430 -0
  260. claude_mpm/services/agents/memory/content_manager.py +376 -0
  261. claude_mpm/services/agents/memory/template_generator.py +468 -0
  262. claude_mpm/services/agents/registry/__init__.py +7 -10
  263. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  264. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  265. claude_mpm/services/async_session_logger.py +187 -153
  266. claude_mpm/services/claude_session_logger.py +87 -72
  267. claude_mpm/services/command_handler_service.py +217 -0
  268. claude_mpm/services/communication/__init__.py +3 -2
  269. claude_mpm/services/core/__init__.py +50 -97
  270. claude_mpm/services/core/base.py +60 -53
  271. claude_mpm/services/core/interfaces/__init__.py +188 -0
  272. claude_mpm/services/core/interfaces/agent.py +351 -0
  273. claude_mpm/services/core/interfaces/communication.py +343 -0
  274. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  275. claude_mpm/services/core/interfaces/service.py +434 -0
  276. claude_mpm/services/core/interfaces.py +19 -944
  277. claude_mpm/services/event_aggregator.py +208 -170
  278. claude_mpm/services/exceptions.py +387 -308
  279. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  280. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  281. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  282. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  283. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  284. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  285. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  286. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  287. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  288. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  289. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  290. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  291. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  292. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  293. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  294. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  295. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  296. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  297. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  298. claude_mpm/services/hook_service.py +106 -114
  299. claude_mpm/services/infrastructure/__init__.py +7 -5
  300. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  301. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  302. claude_mpm/services/infrastructure/logging.py +83 -76
  303. claude_mpm/services/infrastructure/monitoring.py +547 -404
  304. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  305. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  306. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  307. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  308. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  309. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  310. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  311. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  312. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  313. claude_mpm/services/mcp_gateway/main.py +287 -137
  314. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  315. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  316. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  317. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  318. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  319. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  320. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  321. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  322. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  323. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  324. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  325. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  326. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  327. claude_mpm/services/memory/__init__.py +2 -2
  328. claude_mpm/services/memory/builder.py +451 -362
  329. claude_mpm/services/memory/cache/__init__.py +2 -2
  330. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  331. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  332. claude_mpm/services/memory/indexed_memory.py +195 -193
  333. claude_mpm/services/memory/optimizer.py +267 -234
  334. claude_mpm/services/memory/router.py +571 -263
  335. claude_mpm/services/memory_hook_service.py +237 -0
  336. claude_mpm/services/port_manager.py +575 -0
  337. claude_mpm/services/project/__init__.py +3 -3
  338. claude_mpm/services/project/analyzer.py +451 -305
  339. claude_mpm/services/project/registry.py +262 -240
  340. claude_mpm/services/recovery_manager.py +287 -231
  341. claude_mpm/services/response_tracker.py +87 -67
  342. claude_mpm/services/runner_configuration_service.py +587 -0
  343. claude_mpm/services/session_management_service.py +304 -0
  344. claude_mpm/services/socketio/__init__.py +4 -4
  345. claude_mpm/services/socketio/client_proxy.py +174 -0
  346. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  347. claude_mpm/services/socketio/handlers/base.py +44 -30
  348. claude_mpm/services/socketio/handlers/connection.py +166 -64
  349. claude_mpm/services/socketio/handlers/file.py +123 -108
  350. claude_mpm/services/socketio/handlers/git.py +607 -373
  351. claude_mpm/services/socketio/handlers/hook.py +185 -0
  352. claude_mpm/services/socketio/handlers/memory.py +4 -4
  353. claude_mpm/services/socketio/handlers/project.py +4 -4
  354. claude_mpm/services/socketio/handlers/registry.py +53 -38
  355. claude_mpm/services/socketio/server/__init__.py +18 -0
  356. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  357. claude_mpm/services/socketio/server/core.py +399 -0
  358. claude_mpm/services/socketio/server/main.py +323 -0
  359. claude_mpm/services/socketio_client_manager.py +160 -133
  360. claude_mpm/services/socketio_server.py +36 -1885
  361. claude_mpm/services/subprocess_launcher_service.py +316 -0
  362. claude_mpm/services/system_instructions_service.py +258 -0
  363. claude_mpm/services/ticket_manager.py +19 -533
  364. claude_mpm/services/utility_service.py +285 -0
  365. claude_mpm/services/version_control/__init__.py +18 -21
  366. claude_mpm/services/version_control/branch_strategy.py +20 -10
  367. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  368. claude_mpm/services/version_control/git_operations.py +52 -21
  369. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  370. claude_mpm/services/version_control/version_parser.py +145 -125
  371. claude_mpm/services/version_service.py +270 -0
  372. claude_mpm/storage/__init__.py +2 -2
  373. claude_mpm/storage/state_storage.py +177 -181
  374. claude_mpm/ticket_wrapper.py +2 -2
  375. claude_mpm/utils/__init__.py +2 -2
  376. claude_mpm/utils/agent_dependency_loader.py +453 -243
  377. claude_mpm/utils/config_manager.py +157 -118
  378. claude_mpm/utils/console.py +1 -1
  379. claude_mpm/utils/dependency_cache.py +102 -107
  380. claude_mpm/utils/dependency_manager.py +52 -47
  381. claude_mpm/utils/dependency_strategies.py +131 -96
  382. claude_mpm/utils/environment_context.py +110 -102
  383. claude_mpm/utils/error_handler.py +75 -55
  384. claude_mpm/utils/file_utils.py +80 -67
  385. claude_mpm/utils/framework_detection.py +12 -11
  386. claude_mpm/utils/import_migration_example.py +12 -60
  387. claude_mpm/utils/imports.py +48 -45
  388. claude_mpm/utils/path_operations.py +100 -93
  389. claude_mpm/utils/robust_installer.py +172 -164
  390. claude_mpm/utils/session_logging.py +30 -23
  391. claude_mpm/utils/subprocess_utils.py +99 -61
  392. claude_mpm/validation/__init__.py +1 -1
  393. claude_mpm/validation/agent_validator.py +151 -111
  394. claude_mpm/validation/frontmatter_validator.py +92 -71
  395. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/METADATA +90 -22
  396. claude_mpm-4.0.4.dist-info/RECORD +417 -0
  397. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/entry_points.txt +1 -0
  398. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/licenses/LICENSE +1 -1
  399. claude_mpm/cli/commands/run_guarded.py +0 -511
  400. claude_mpm/config/memory_guardian_config.py +0 -325
  401. claude_mpm/config/memory_guardian_yaml.py +0 -335
  402. claude_mpm/core/config_paths.py +0 -150
  403. claude_mpm/core/memory_aware_runner.py +0 -353
  404. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  405. claude_mpm/deployment_paths.py +0 -261
  406. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  407. claude_mpm/models/state_models.py +0 -433
  408. claude_mpm/services/agent/__init__.py +0 -24
  409. claude_mpm/services/agent/deployment.py +0 -2548
  410. claude_mpm/services/agent/management.py +0 -598
  411. claude_mpm/services/agent/registry.py +0 -813
  412. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  413. claude_mpm/services/communication/socketio.py +0 -1935
  414. claude_mpm/services/communication/websocket.py +0 -479
  415. claude_mpm/services/framework_claude_md_generator.py +0 -624
  416. claude_mpm/services/health_monitor.py +0 -893
  417. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  418. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  419. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  420. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  421. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  422. claude_mpm/services/infrastructure/state_manager.py +0 -774
  423. claude_mpm/services/mcp_gateway/manager.py +0 -334
  424. claude_mpm/services/optimized_hook_service.py +0 -542
  425. claude_mpm/services/project_analyzer.py +0 -864
  426. claude_mpm/services/project_registry.py +0 -608
  427. claude_mpm/services/standalone_socketio_server.py +0 -1300
  428. claude_mpm/services/ticket_manager_di.py +0 -318
  429. claude_mpm/services/ticketing_service_original.py +0 -510
  430. claude_mpm/utils/paths.py +0 -395
  431. claude_mpm/utils/platform_memory.py +0 -524
  432. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  433. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.dist-info}/WHEEL +0 -0
  434. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.4.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}")