claude-mpm 3.9.9__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 (411) 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 +155 -0
  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 +90 -49
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +21 -18
  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 +143 -762
  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 -1150
  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 +217 -0
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +36 -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 +571 -0
  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 +40 -23
  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 +14 -21
  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 +97 -93
  298. claude_mpm/services/mcp_gateway/main.py +307 -127
  299. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  302. claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
  303. claude_mpm/services/mcp_gateway/server/{mcp_server.py → mcp_gateway.py} +149 -153
  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 +110 -121
  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 +20 -534
  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 +9 -0
  358. claude_mpm/storage/state_storage.py +552 -0
  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.9.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +51 -2
  381. claude_mpm-4.0.3.dist-info/RECORD +402 -0
  382. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
  383. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
  384. claude_mpm/config/memory_guardian_config.py +0 -325
  385. claude_mpm/core/config_paths.py +0 -150
  386. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  387. claude_mpm/deployment_paths.py +0 -261
  388. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  389. claude_mpm/models/state_models.py +0 -433
  390. claude_mpm/services/agent/__init__.py +0 -24
  391. claude_mpm/services/agent/deployment.py +0 -2548
  392. claude_mpm/services/agent/management.py +0 -598
  393. claude_mpm/services/agent/registry.py +0 -813
  394. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  395. claude_mpm/services/communication/socketio.py +0 -1935
  396. claude_mpm/services/communication/websocket.py +0 -479
  397. claude_mpm/services/framework_claude_md_generator.py +0 -624
  398. claude_mpm/services/health_monitor.py +0 -893
  399. claude_mpm/services/infrastructure/memory_guardian.py +0 -770
  400. claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
  401. claude_mpm/services/optimized_hook_service.py +0 -542
  402. claude_mpm/services/project_analyzer.py +0 -864
  403. claude_mpm/services/project_registry.py +0 -608
  404. claude_mpm/services/standalone_socketio_server.py +0 -1300
  405. claude_mpm/services/ticket_manager_di.py +0 -318
  406. claude_mpm/services/ticketing_service_original.py +0 -510
  407. claude_mpm/utils/paths.py +0 -395
  408. claude_mpm/utils/platform_memory.py +0 -524
  409. claude_mpm-3.9.9.dist-info/RECORD +0 -293
  410. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
  411. {claude_mpm-3.9.9.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
@@ -22,24 +22,23 @@ Performance Impact:
22
22
  - Reduces redundant file I/O operations
23
23
 
24
24
  Usage:
25
- from claude_pm.services.shared_prompt_cache import SharedPromptCache
26
-
25
+ from claude_mpm.services.shared_prompt_cache import SharedPromptCache
26
+
27
27
  # Get singleton instance
28
28
  cache = SharedPromptCache.get_instance()
29
-
29
+
30
30
  # Cache prompt data
31
31
  cache.set("engineer:profile", prompt_data, ttl=300)
32
-
32
+
33
33
  # Retrieve cached data
34
34
  prompt_data = cache.get("engineer:profile")
35
-
35
+
36
36
  # Invalidate specific cache entries
37
37
  cache.invalidate("engineer:profile")
38
38
  """
39
39
 
40
40
  import asyncio
41
41
  import json
42
- import logging
43
42
  import threading
44
43
  import time
45
44
  import weakref
@@ -47,7 +46,6 @@ from collections import OrderedDict
47
46
  from dataclasses import dataclass, field
48
47
  from datetime import datetime, timedelta
49
48
  from functools import wraps
50
- from pathlib import Path
51
49
  from threading import RLock
52
50
  from typing import Any, Dict, List, Optional, Set, Tuple, Union
53
51
 
@@ -57,7 +55,7 @@ from claude_mpm.core.base_service import BaseService, ServiceHealth, ServiceMetr
57
55
  @dataclass
58
56
  class CacheEntry:
59
57
  """Cache entry with TTL and metadata."""
60
-
58
+
61
59
  key: str
62
60
  value: Any
63
61
  created_at: float
@@ -66,19 +64,19 @@ class CacheEntry:
66
64
  last_accessed: float = field(default_factory=time.time)
67
65
  size_bytes: int = 0
68
66
  metadata: Dict[str, Any] = field(default_factory=dict)
69
-
67
+
70
68
  @property
71
69
  def is_expired(self) -> bool:
72
70
  """Check if cache entry has expired."""
73
71
  if self.ttl is None:
74
72
  return False
75
73
  return time.time() > (self.created_at + self.ttl)
76
-
74
+
77
75
  @property
78
76
  def age_seconds(self) -> float:
79
77
  """Get age of cache entry in seconds."""
80
78
  return time.time() - self.created_at
81
-
79
+
82
80
  def touch(self) -> None:
83
81
  """Update access metrics."""
84
82
  self.access_count += 1
@@ -88,7 +86,7 @@ class CacheEntry:
88
86
  @dataclass
89
87
  class CacheMetrics:
90
88
  """Cache performance metrics."""
91
-
89
+
92
90
  hits: int = 0
93
91
  misses: int = 0
94
92
  sets: int = 0
@@ -98,13 +96,13 @@ class CacheMetrics:
98
96
  entry_count: int = 0
99
97
  evictions: int = 0
100
98
  expired_removals: int = 0
101
-
99
+
102
100
  @property
103
101
  def hit_rate(self) -> float:
104
102
  """Calculate cache hit rate."""
105
103
  total = self.hits + self.misses
106
104
  return self.hits / total if total > 0 else 0.0
107
-
105
+
108
106
  @property
109
107
  def miss_rate(self) -> float:
110
108
  """Calculate cache miss rate."""
@@ -114,59 +112,75 @@ class CacheMetrics:
114
112
  class SharedPromptCache(BaseService):
115
113
  """
116
114
  Shared Prompt Cache Service with Singleton Pattern
117
-
115
+
118
116
  Thread-safe, high-performance caching service for subprocess agent prompts.
119
117
  Implements LRU eviction with TTL support and comprehensive metrics.
120
118
  """
121
-
122
- _instance: Optional['SharedPromptCache'] = None
119
+
120
+ _instance: Optional["SharedPromptCache"] = None
123
121
  _lock = threading.Lock()
124
-
122
+
125
123
  def __init__(self, config: Optional[Dict[str, Any]] = None):
126
124
  """Initialize the shared cache service."""
127
125
  # Singleton pattern enforcement
128
126
  if SharedPromptCache._instance is not None:
129
- raise RuntimeError("SharedPromptCache is a singleton. Use get_instance() instead.")
130
-
127
+ raise RuntimeError(
128
+ "SharedPromptCache is a singleton. Use get_instance() instead."
129
+ )
130
+
131
131
  super().__init__("shared_prompt_cache", config)
132
-
132
+
133
133
  # Cache configuration
134
- self.max_size = self.get_config("max_size", 500) # Reduced maximum cache entries
135
- self.max_memory_mb = self.get_config("max_memory_mb", 50) # Reduced maximum memory usage
136
- self.default_ttl = self.get_config("default_ttl", 300) # 5 minutes default TTL (was 30)
137
- self.cleanup_interval = self.get_config("cleanup_interval", 60) # 1 minute cleanup (was 5)
134
+ self.max_size = self.get_config(
135
+ "max_size", 500
136
+ ) # Reduced maximum cache entries
137
+ self.max_memory_mb = self.get_config(
138
+ "max_memory_mb", 50
139
+ ) # Reduced maximum memory usage
140
+ self.default_ttl = self.get_config(
141
+ "default_ttl", 300
142
+ ) # 5 minutes default TTL (was 30)
143
+ self.cleanup_interval = self.get_config(
144
+ "cleanup_interval", 60
145
+ ) # 1 minute cleanup (was 5)
138
146
  self.enable_metrics = self.get_config("enable_metrics", True)
139
-
147
+
140
148
  # Memory pressure handling
141
- self.memory_pressure_threshold = 0.8 # 80% of max memory triggers aggressive cleanup
149
+ self.memory_pressure_threshold = (
150
+ 0.8 # 80% of max memory triggers aggressive cleanup
151
+ )
142
152
  self.aggressive_cleanup_active = False
143
-
153
+
144
154
  # Cache storage - OrderedDict for LRU behavior
145
155
  self._cache: OrderedDict[str, CacheEntry] = OrderedDict()
146
156
  self._cache_lock = RLock() # Reentrant lock for nested operations
147
-
157
+
148
158
  # Metrics and monitoring
149
159
  self._metrics = CacheMetrics()
150
160
  self._metrics_lock = threading.Lock()
151
-
161
+
152
162
  # Background task tracking
153
163
  self._cleanup_task: Optional[asyncio.Task] = None
154
-
164
+
155
165
  # Cache invalidation tracking
156
166
  self._invalidation_callbacks: Dict[str, List[callable]] = {}
157
167
  self._namespace_dependencies: Dict[str, Set[str]] = {}
158
-
159
- self.logger.info(f"SharedPromptCache initialized with max_size={self.max_size}, "
160
- f"max_memory_mb={self.max_memory_mb}, default_ttl={self.default_ttl}s")
161
-
168
+
169
+ self.logger.info(
170
+ f"SharedPromptCache initialized with max_size={self.max_size}, "
171
+ f"max_memory_mb={self.max_memory_mb}, default_ttl={self.default_ttl}s"
172
+ )
173
+
162
174
  @classmethod
163
- def get_instance(cls, config: Optional[Dict[str, Any]] = None) -> 'SharedPromptCache':
175
+ def get_instance(
176
+ cls, config: Optional[Dict[str, Any]] = None
177
+ ) -> "SharedPromptCache":
164
178
  """
165
179
  Get the singleton instance of SharedPromptCache.
166
-
180
+
167
181
  Args:
168
182
  config: Optional configuration (only used on first call)
169
-
183
+
170
184
  Returns:
171
185
  Singleton instance of SharedPromptCache
172
186
  """
@@ -175,7 +189,7 @@ class SharedPromptCache(BaseService):
175
189
  if cls._instance is None:
176
190
  cls._instance = cls(config)
177
191
  return cls._instance
178
-
192
+
179
193
  @classmethod
180
194
  def reset_instance(cls) -> None:
181
195
  """Reset singleton instance (for testing purposes)."""
@@ -184,85 +198,95 @@ class SharedPromptCache(BaseService):
184
198
  if cls._instance.running:
185
199
  asyncio.create_task(cls._instance.stop())
186
200
  cls._instance = None
187
-
201
+
188
202
  async def _initialize(self) -> None:
189
203
  """Initialize the cache service."""
190
204
  self.logger.info("Initializing SharedPromptCache service...")
191
-
205
+
192
206
  # Start cleanup task
193
207
  self._cleanup_task = asyncio.create_task(self._cleanup_expired_entries())
194
-
208
+
195
209
  # Register with memory pressure coordinator
196
210
  try:
197
211
  from .memory_pressure_coordinator import register_service_cleanup
198
- await register_service_cleanup("shared_prompt_cache", self.handle_memory_pressure)
212
+
213
+ await register_service_cleanup(
214
+ "shared_prompt_cache", self.handle_memory_pressure
215
+ )
199
216
  self.logger.info("Registered with memory pressure coordinator")
200
217
  except Exception as e:
201
- self.logger.warning(f"Failed to register with memory pressure coordinator: {e}")
202
-
218
+ self.logger.warning(
219
+ f"Failed to register with memory pressure coordinator: {e}"
220
+ )
221
+
203
222
  # Note: Metrics collection is handled by parent class
204
223
  # Custom metrics are collected in _collect_custom_metrics()
205
-
224
+
206
225
  self.logger.info("SharedPromptCache service initialized successfully")
207
-
226
+
208
227
  async def _cleanup(self) -> None:
209
228
  """Cleanup cache service resources."""
210
229
  self.logger.info("Cleaning up SharedPromptCache service...")
211
-
230
+
212
231
  # Cancel background tasks
213
232
  if self._cleanup_task:
214
233
  self._cleanup_task.cancel()
215
-
234
+
216
235
  # Clear cache
217
236
  with self._cache_lock:
218
237
  self._cache.clear()
219
-
238
+
220
239
  self.logger.info("SharedPromptCache service cleaned up")
221
-
240
+
222
241
  async def _health_check(self) -> Dict[str, bool]:
223
242
  """Perform cache-specific health checks."""
224
243
  checks = {}
225
-
244
+
226
245
  try:
227
246
  # Test cache operations
228
247
  test_key = f"__health_check_{time.time()}"
229
248
  test_value = {"test": True, "timestamp": time.time()}
230
-
249
+
231
250
  # Test set operation
232
251
  self.set(test_key, test_value, ttl=5)
233
252
  checks["cache_set"] = True
234
-
253
+
235
254
  # Test get operation
236
255
  retrieved = self.get(test_key)
237
256
  checks["cache_get"] = retrieved is not None and retrieved["test"] is True
238
-
257
+
239
258
  # Test delete operation
240
259
  self.delete(test_key)
241
260
  checks["cache_delete"] = self.get(test_key) is None
242
-
261
+
243
262
  # Check memory usage
244
263
  checks["memory_usage_ok"] = self._get_memory_usage_mb() < self.max_memory_mb
245
-
264
+
246
265
  # Check cache size
247
266
  checks["cache_size_ok"] = len(self._cache) <= self.max_size
248
-
267
+
249
268
  except Exception as e:
250
269
  self.logger.error(f"Cache health check failed: {e}")
251
270
  checks["cache_operations"] = False
252
-
271
+
253
272
  return checks
254
-
255
- def set(self, key: str, value: Any, ttl: Optional[float] = None,
256
- metadata: Optional[Dict[str, Any]] = None) -> bool:
273
+
274
+ def set(
275
+ self,
276
+ key: str,
277
+ value: Any,
278
+ ttl: Optional[float] = None,
279
+ metadata: Optional[Dict[str, Any]] = None,
280
+ ) -> bool:
257
281
  """
258
282
  Set a cache entry with optional TTL.
259
-
283
+
260
284
  Args:
261
285
  key: Cache key
262
286
  value: Value to cache
263
287
  ttl: Time to live in seconds (uses default_ttl if None)
264
288
  metadata: Optional metadata for the cache entry
265
-
289
+
266
290
  Returns:
267
291
  True if successful, False otherwise
268
292
  """
@@ -271,10 +295,10 @@ class SharedPromptCache(BaseService):
271
295
  # Use default TTL if not specified
272
296
  if ttl is None:
273
297
  ttl = self.default_ttl
274
-
298
+
275
299
  # Calculate entry size
276
300
  size_bytes = self._calculate_size(value)
277
-
301
+
278
302
  # Create cache entry
279
303
  entry = CacheEntry(
280
304
  key=key,
@@ -282,54 +306,56 @@ class SharedPromptCache(BaseService):
282
306
  created_at=time.time(),
283
307
  ttl=ttl,
284
308
  size_bytes=size_bytes,
285
- metadata=metadata or {}
309
+ metadata=metadata or {},
286
310
  )
287
-
311
+
288
312
  # Check if we need to evict entries
289
313
  self._ensure_cache_capacity(size_bytes)
290
-
314
+
291
315
  # Remove existing entry if present
292
316
  if key in self._cache:
293
317
  old_entry = self._cache.pop(key)
294
318
  with self._metrics_lock:
295
319
  self._metrics.size_bytes -= old_entry.size_bytes
296
-
320
+
297
321
  # Add new entry (to end for LRU)
298
322
  self._cache[key] = entry
299
-
323
+
300
324
  # Update metrics
301
325
  with self._metrics_lock:
302
326
  self._metrics.sets += 1
303
327
  self._metrics.size_bytes += size_bytes
304
328
  self._metrics.entry_count = len(self._cache)
305
-
306
- self.logger.debug(f"Cached key '{key}' with TTL {ttl}s, size {size_bytes} bytes")
329
+
330
+ self.logger.debug(
331
+ f"Cached key '{key}' with TTL {ttl}s, size {size_bytes} bytes"
332
+ )
307
333
  return True
308
-
334
+
309
335
  except Exception as e:
310
336
  self.logger.error(f"Failed to set cache key '{key}': {e}")
311
337
  return False
312
-
338
+
313
339
  def get(self, key: str) -> Optional[Any]:
314
340
  """
315
341
  Get a cache entry by key.
316
-
342
+
317
343
  Args:
318
344
  key: Cache key to retrieve
319
-
345
+
320
346
  Returns:
321
347
  Cached value if found and not expired, None otherwise
322
348
  """
323
349
  try:
324
350
  with self._cache_lock:
325
351
  entry = self._cache.get(key)
326
-
352
+
327
353
  if entry is None:
328
354
  # Cache miss
329
355
  with self._metrics_lock:
330
356
  self._metrics.misses += 1
331
357
  return None
332
-
358
+
333
359
  if entry.is_expired:
334
360
  # Entry expired, remove it
335
361
  self._remove_entry(key, entry)
@@ -337,118 +363,124 @@ class SharedPromptCache(BaseService):
337
363
  self._metrics.misses += 1
338
364
  self._metrics.expired_removals += 1
339
365
  return None
340
-
366
+
341
367
  # Cache hit - update access metrics and move to end (LRU)
342
368
  entry.touch()
343
369
  self._cache.move_to_end(key)
344
-
370
+
345
371
  with self._metrics_lock:
346
372
  self._metrics.hits += 1
347
-
348
- self.logger.debug(f"Cache hit for key '{key}' (age: {entry.age_seconds:.1f}s)")
373
+
374
+ self.logger.debug(
375
+ f"Cache hit for key '{key}' (age: {entry.age_seconds:.1f}s)"
376
+ )
349
377
  return entry.value
350
-
378
+
351
379
  except Exception as e:
352
380
  self.logger.error(f"Failed to get cache key '{key}': {e}")
353
381
  with self._metrics_lock:
354
382
  self._metrics.misses += 1
355
383
  return None
356
-
384
+
357
385
  def delete(self, key: str) -> bool:
358
386
  """
359
387
  Delete a cache entry.
360
-
388
+
361
389
  Args:
362
390
  key: Cache key to delete
363
-
391
+
364
392
  Returns:
365
393
  True if deleted, False if not found
366
394
  """
367
395
  try:
368
396
  with self._cache_lock:
369
397
  entry = self._cache.pop(key, None)
370
-
398
+
371
399
  if entry is not None:
372
400
  with self._metrics_lock:
373
401
  self._metrics.deletes += 1
374
402
  self._metrics.size_bytes -= entry.size_bytes
375
403
  self._metrics.entry_count = len(self._cache)
376
-
404
+
377
405
  self.logger.debug(f"Deleted cache key '{key}'")
378
406
  return True
379
-
407
+
380
408
  return False
381
-
409
+
382
410
  except Exception as e:
383
411
  self.logger.error(f"Failed to delete cache key '{key}': {e}")
384
412
  return False
385
-
413
+
386
414
  def invalidate(self, pattern: str) -> int:
387
415
  """
388
416
  Invalidate cache entries matching a pattern.
389
-
417
+
390
418
  Args:
391
419
  pattern: Pattern to match keys (supports wildcards *)
392
-
420
+
393
421
  Returns:
394
422
  Number of entries invalidated
395
423
  """
396
424
  try:
397
425
  import fnmatch
398
-
426
+
399
427
  invalidated = 0
400
-
428
+
401
429
  with self._cache_lock:
402
430
  keys_to_remove = []
403
-
431
+
404
432
  for key in self._cache.keys():
405
433
  if fnmatch.fnmatch(key, pattern):
406
434
  keys_to_remove.append(key)
407
-
435
+
408
436
  for key in keys_to_remove:
409
437
  entry = self._cache.pop(key)
410
438
  with self._metrics_lock:
411
439
  self._metrics.size_bytes -= entry.size_bytes
412
440
  invalidated += 1
413
-
441
+
414
442
  with self._metrics_lock:
415
443
  self._metrics.invalidations += invalidated
416
444
  self._metrics.entry_count = len(self._cache)
417
-
418
- self.logger.info(f"Invalidated {invalidated} cache entries matching pattern '{pattern}'")
419
-
445
+
446
+ self.logger.info(
447
+ f"Invalidated {invalidated} cache entries matching pattern '{pattern}'"
448
+ )
449
+
420
450
  # Trigger invalidation callbacks
421
451
  self._trigger_invalidation_callbacks(pattern)
422
-
452
+
423
453
  return invalidated
424
-
454
+
425
455
  except Exception as e:
426
456
  self.logger.error(f"Failed to invalidate pattern '{pattern}': {e}")
427
457
  return 0
428
-
458
+
429
459
  def clear(self) -> None:
430
460
  """Clear all cache entries."""
431
461
  try:
432
462
  with self._cache_lock:
433
463
  entry_count = len(self._cache)
434
464
  self._cache.clear()
435
-
465
+
436
466
  with self._metrics_lock:
437
467
  self._metrics.size_bytes = 0
438
468
  self._metrics.entry_count = 0
439
469
  self._metrics.invalidations += entry_count
440
-
470
+
441
471
  self.logger.info(f"Cleared all {entry_count} cache entries")
442
-
472
+
443
473
  except Exception as e:
444
474
  self.logger.error(f"Failed to clear cache: {e}")
445
-
475
+
446
476
  def get_metrics(self) -> Dict[str, Any]:
447
477
  """Get current cache metrics."""
448
478
  with self._metrics_lock:
449
479
  size_mb = self._metrics.size_bytes / (1024 * 1024)
450
- memory_usage_percent = (size_mb / self.max_memory_mb * 100) if self.max_memory_mb > 0 else 0
451
-
480
+ memory_usage_percent = (
481
+ (size_mb / self.max_memory_mb * 100) if self.max_memory_mb > 0 else 0
482
+ )
483
+
452
484
  return {
453
485
  "hits": self._metrics.hits,
454
486
  "misses": self._metrics.misses,
@@ -467,15 +499,15 @@ class SharedPromptCache(BaseService):
467
499
  "memory_usage_percent": memory_usage_percent,
468
500
  "memory_pressure": memory_usage_percent > 80, # Flag high memory usage
469
501
  "ttl_default": self.default_ttl,
470
- "cleanup_interval": self.cleanup_interval
502
+ "cleanup_interval": self.cleanup_interval,
471
503
  }
472
-
504
+
473
505
  def get_cache_info(self) -> Dict[str, Any]:
474
506
  """Get detailed cache information."""
475
507
  with self._cache_lock:
476
508
  entries_info = []
477
509
  total_size = 0
478
-
510
+
479
511
  for key, entry in self._cache.items():
480
512
  entry_info = {
481
513
  "key": key,
@@ -484,30 +516,30 @@ class SharedPromptCache(BaseService):
484
516
  "size_bytes": entry.size_bytes,
485
517
  "is_expired": entry.is_expired,
486
518
  "ttl": entry.ttl,
487
- "metadata": entry.metadata
519
+ "metadata": entry.metadata,
488
520
  }
489
521
  entries_info.append(entry_info)
490
522
  total_size += entry.size_bytes
491
-
523
+
492
524
  return {
493
525
  "total_entries": len(self._cache),
494
526
  "total_size_bytes": total_size,
495
527
  "total_size_mb": total_size / (1024 * 1024),
496
528
  "entries": entries_info,
497
- "metrics": self.get_metrics()
529
+ "metrics": self.get_metrics(),
498
530
  }
499
-
531
+
500
532
  def register_invalidation_callback(self, pattern: str, callback: callable) -> None:
501
533
  """Register a callback for cache invalidation events."""
502
534
  if pattern not in self._invalidation_callbacks:
503
535
  self._invalidation_callbacks[pattern] = []
504
536
  self._invalidation_callbacks[pattern].append(callback)
505
-
537
+
506
538
  def _ensure_cache_capacity(self, new_entry_size: int) -> None:
507
539
  """Ensure cache has capacity for new entry."""
508
540
  current_memory_mb = self._get_memory_usage_mb()
509
541
  max_memory_bytes = self.max_memory_mb * 1024 * 1024
510
-
542
+
511
543
  # Check if we're under memory pressure
512
544
  memory_usage_ratio = current_memory_mb / self.max_memory_mb
513
545
  if memory_usage_ratio > self.memory_pressure_threshold:
@@ -516,61 +548,63 @@ class SharedPromptCache(BaseService):
516
548
  while self._metrics.size_bytes > target_memory_bytes:
517
549
  if not self._evict_lru_entry():
518
550
  break
519
- self.logger.warning(f"Memory pressure detected ({memory_usage_ratio:.1%}), "
520
- f"aggressively cleaned cache to {self._get_memory_usage_mb():.1f} MB")
521
-
551
+ self.logger.warning(
552
+ f"Memory pressure detected ({memory_usage_ratio:.1%}), "
553
+ f"aggressively cleaned cache to {self._get_memory_usage_mb():.1f} MB"
554
+ )
555
+
522
556
  # Check memory limit
523
557
  while (self._metrics.size_bytes + new_entry_size) > max_memory_bytes:
524
558
  if not self._evict_lru_entry():
525
559
  break
526
-
560
+
527
561
  # Check size limit
528
562
  while len(self._cache) >= self.max_size:
529
563
  if not self._evict_lru_entry():
530
564
  break
531
-
565
+
532
566
  def _evict_lru_entry(self) -> bool:
533
567
  """Evict least recently used entry."""
534
568
  if not self._cache:
535
569
  return False
536
-
570
+
537
571
  # Get LRU entry (first in OrderedDict)
538
572
  key, entry = next(iter(self._cache.items()))
539
573
  self._remove_entry(key, entry)
540
-
574
+
541
575
  with self._metrics_lock:
542
576
  self._metrics.evictions += 1
543
-
577
+
544
578
  self.logger.debug(f"Evicted LRU entry '{key}' (age: {entry.age_seconds:.1f}s)")
545
579
  return True
546
-
580
+
547
581
  def _remove_entry(self, key: str, entry: CacheEntry) -> None:
548
582
  """Remove entry from cache and update metrics."""
549
583
  self._cache.pop(key, None)
550
584
  with self._metrics_lock:
551
585
  self._metrics.size_bytes -= entry.size_bytes
552
586
  self._metrics.entry_count = len(self._cache)
553
-
587
+
554
588
  def _calculate_size(self, value: Any) -> int:
555
589
  """Calculate approximate size of value in bytes."""
556
590
  try:
557
591
  # Use JSON serialization as approximation
558
- return len(json.dumps(value, default=str).encode('utf-8'))
592
+ return len(json.dumps(value, default=str).encode("utf-8"))
559
593
  except Exception:
560
594
  # Fallback to string representation
561
- return len(str(value).encode('utf-8'))
562
-
595
+ return len(str(value).encode("utf-8"))
596
+
563
597
  def _get_memory_usage_mb(self) -> float:
564
598
  """Get current memory usage in MB."""
565
599
  return self._metrics.size_bytes / (1024 * 1024)
566
-
600
+
567
601
  async def handle_memory_pressure(self, severity: str = "warning") -> Dict[str, Any]:
568
602
  """
569
603
  Handle memory pressure by aggressively cleaning cache.
570
-
604
+
571
605
  Args:
572
606
  severity: "warning" or "critical" level of memory pressure
573
-
607
+
574
608
  Returns:
575
609
  Dict with cleanup statistics
576
610
  """
@@ -578,9 +612,9 @@ class SharedPromptCache(BaseService):
578
612
  "entries_before": len(self._cache),
579
613
  "memory_before_mb": self._get_memory_usage_mb(),
580
614
  "entries_removed": 0,
581
- "memory_freed_mb": 0
615
+ "memory_freed_mb": 0,
582
616
  }
583
-
617
+
584
618
  with self._cache_lock:
585
619
  if severity == "critical":
586
620
  # Critical: Clear 75% of cache
@@ -588,21 +622,21 @@ class SharedPromptCache(BaseService):
588
622
  else:
589
623
  # Warning: Clear 50% of cache
590
624
  target_entries = int(len(self._cache) * 0.5)
591
-
625
+
592
626
  # Remove oldest entries first
593
627
  while len(self._cache) > target_entries:
594
628
  if not self._evict_lru_entry():
595
629
  break
596
630
  stats["entries_removed"] += 1
597
-
631
+
598
632
  # Force cleanup of expired entries
599
633
  expired_count = 0
600
634
  keys_to_remove = []
601
-
635
+
602
636
  for key, entry in self._cache.items():
603
637
  if entry.is_expired:
604
638
  keys_to_remove.append(key)
605
-
639
+
606
640
  for key in keys_to_remove:
607
641
  entry = self._cache.pop(key)
608
642
  with self._metrics_lock:
@@ -610,24 +644,26 @@ class SharedPromptCache(BaseService):
610
644
  self._metrics.expired_removals += 1
611
645
  expired_count += 1
612
646
  stats["entries_removed"] += 1
613
-
647
+
614
648
  if expired_count > 0:
615
649
  with self._metrics_lock:
616
650
  self._metrics.entry_count = len(self._cache)
617
-
651
+
618
652
  stats["entries_after"] = len(self._cache)
619
653
  stats["memory_after_mb"] = self._get_memory_usage_mb()
620
654
  stats["memory_freed_mb"] = stats["memory_before_mb"] - stats["memory_after_mb"]
621
-
622
- self.logger.info(f"Memory pressure ({severity}): Removed {stats['entries_removed']} entries, "
623
- f"freed {stats['memory_freed_mb']:.2f} MB")
624
-
655
+
656
+ self.logger.info(
657
+ f"Memory pressure ({severity}): Removed {stats['entries_removed']} entries, "
658
+ f"freed {stats['memory_freed_mb']:.2f} MB"
659
+ )
660
+
625
661
  return stats
626
-
662
+
627
663
  def _trigger_invalidation_callbacks(self, pattern: str) -> None:
628
664
  """Trigger invalidation callbacks for pattern."""
629
665
  import fnmatch
630
-
666
+
631
667
  for callback_pattern, callbacks in self._invalidation_callbacks.items():
632
668
  if fnmatch.fnmatch(pattern, callback_pattern):
633
669
  for callback in callbacks:
@@ -635,43 +671,45 @@ class SharedPromptCache(BaseService):
635
671
  callback(pattern)
636
672
  except Exception as e:
637
673
  self.logger.error(f"Invalidation callback failed: {e}")
638
-
674
+
639
675
  async def _cleanup_expired_entries(self) -> None:
640
676
  """Background task to clean up expired entries."""
641
677
  while not self._stop_event.is_set():
642
678
  try:
643
679
  expired_count = 0
644
-
680
+
645
681
  with self._cache_lock:
646
682
  keys_to_remove = []
647
-
683
+
648
684
  for key, entry in self._cache.items():
649
685
  if entry.is_expired:
650
686
  keys_to_remove.append(key)
651
-
687
+
652
688
  for key in keys_to_remove:
653
689
  entry = self._cache.pop(key)
654
690
  with self._metrics_lock:
655
691
  self._metrics.size_bytes -= entry.size_bytes
656
692
  self._metrics.expired_removals += 1
657
693
  expired_count += 1
658
-
694
+
659
695
  if expired_count > 0:
660
696
  with self._metrics_lock:
661
697
  self._metrics.entry_count = len(self._cache)
662
-
698
+
663
699
  if expired_count > 0:
664
- self.logger.debug(f"Cleaned up {expired_count} expired cache entries")
665
-
700
+ self.logger.debug(
701
+ f"Cleaned up {expired_count} expired cache entries"
702
+ )
703
+
666
704
  # Wait for next cleanup interval
667
705
  await asyncio.sleep(self.cleanup_interval)
668
-
706
+
669
707
  except asyncio.CancelledError:
670
708
  break
671
709
  except Exception as e:
672
710
  self.logger.error(f"Cache cleanup task error: {e}")
673
711
  await asyncio.sleep(self.cleanup_interval)
674
-
712
+
675
713
  async def _collect_custom_metrics(self) -> None:
676
714
  """Collect custom metrics for the service."""
677
715
  try:
@@ -682,58 +720,60 @@ class SharedPromptCache(BaseService):
682
720
  cache_misses=metrics["misses"],
683
721
  cache_hit_rate=metrics["hit_rate"],
684
722
  cache_size_mb=metrics["size_mb"],
685
- cache_entries=metrics["entry_count"]
723
+ cache_entries=metrics["entry_count"],
686
724
  )
687
725
  except Exception as e:
688
726
  self.logger.warning(f"Failed to collect cache metrics: {e}")
689
727
 
690
728
 
691
729
  # Decorator for caching function results
692
- def cache_result(key_pattern: str, ttl: Optional[float] = None,
693
- namespace: Optional[str] = None):
730
+ def cache_result(
731
+ key_pattern: str, ttl: Optional[float] = None, namespace: Optional[str] = None
732
+ ):
694
733
  """
695
734
  Decorator to cache function results in SharedPromptCache.
696
-
735
+
697
736
  Args:
698
737
  key_pattern: Pattern for cache key (can use {args} placeholders)
699
738
  ttl: Time to live for cached result
700
739
  namespace: Optional namespace for cache keys
701
-
740
+
702
741
  Example:
703
742
  @cache_result("agent_profile:{agent_name}", ttl=300)
704
743
  def load_agent_profile(agent_name: str):
705
744
  # Load profile logic
706
745
  return profile_data
707
746
  """
747
+
708
748
  def decorator(func):
709
749
  @wraps(func)
710
750
  def wrapper(*args, **kwargs):
711
751
  # Generate cache key
712
752
  import hashlib
713
-
753
+
714
754
  # Create key from pattern and args
715
755
  cache_key = key_pattern.format(
716
- **kwargs,
717
- args_hash=hashlib.md5(str(args).encode()).hexdigest()[:8]
756
+ **kwargs, args_hash=hashlib.md5(str(args).encode()).hexdigest()[:8]
718
757
  )
719
-
758
+
720
759
  if namespace:
721
760
  cache_key = f"{namespace}:{cache_key}"
722
-
761
+
723
762
  # Try to get from cache
724
763
  cache = SharedPromptCache.get_instance()
725
764
  result = cache.get(cache_key)
726
-
765
+
727
766
  if result is not None:
728
767
  return result
729
-
768
+
730
769
  # Call function and cache result
731
770
  result = func(*args, **kwargs)
732
771
  cache.set(cache_key, result, ttl=ttl)
733
-
772
+
734
773
  return result
735
-
774
+
736
775
  return wrapper
776
+
737
777
  return decorator
738
778
 
739
779
 
@@ -755,65 +795,63 @@ if __name__ == "__main__":
755
795
  """Demonstrate SharedPromptCache usage."""
756
796
  print("šŸš€ SharedPromptCache Demo")
757
797
  print("=" * 50)
758
-
798
+
759
799
  # Get cache instance
760
- cache = SharedPromptCache.get_instance({
761
- "max_size": 100,
762
- "max_memory_mb": 10,
763
- "default_ttl": 60
764
- })
765
-
800
+ cache = SharedPromptCache.get_instance(
801
+ {"max_size": 100, "max_memory_mb": 10, "default_ttl": 60}
802
+ )
803
+
766
804
  # Start the service
767
805
  await cache.start()
768
-
806
+
769
807
  try:
770
808
  # Test basic operations
771
809
  print("\nšŸ“ Testing basic cache operations...")
772
-
810
+
773
811
  # Set some data
774
812
  cache.set("test:key1", {"data": "value1", "type": "test"})
775
813
  cache.set("test:key2", {"data": "value2", "type": "test"}, ttl=5)
776
-
814
+
777
815
  # Get data
778
816
  result1 = cache.get("test:key1")
779
817
  result2 = cache.get("test:key2")
780
818
  print(f"Retrieved: {result1}, {result2}")
781
-
819
+
782
820
  # Test metrics
783
821
  print("\nšŸ“Š Cache metrics:")
784
822
  metrics = cache.get_metrics()
785
823
  for key, value in metrics.items():
786
824
  print(f" {key}: {value}")
787
-
825
+
788
826
  # Test invalidation
789
827
  print("\nšŸ—‘ļø Testing invalidation...")
790
828
  cache.invalidate("test:*")
791
-
829
+
792
830
  # Test decorator
793
831
  print("\nšŸŽÆ Testing cache decorator...")
794
-
832
+
795
833
  @cache_result("demo:{name}", ttl=30)
796
834
  def get_demo_data(name: str):
797
835
  print(f"Computing data for {name}...")
798
836
  return {"name": name, "timestamp": time.time()}
799
-
837
+
800
838
  # First call (cache miss)
801
839
  data1 = get_demo_data("test")
802
840
  print(f"First call: {data1}")
803
-
841
+
804
842
  # Second call (cache hit)
805
843
  data2 = get_demo_data("test")
806
844
  print(f"Second call: {data2}")
807
-
845
+
808
846
  # Final metrics
809
847
  print("\nšŸ“Š Final metrics:")
810
848
  final_metrics = cache.get_metrics()
811
849
  for key, value in final_metrics.items():
812
850
  print(f" {key}: {value}")
813
-
851
+
814
852
  finally:
815
853
  await cache.stop()
816
854
  print("\nāœ… Demo completed")
817
-
855
+
818
856
  # Run demo
819
- asyncio.run(demo())
857
+ asyncio.run(demo())