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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (419) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +2 -2
  3. claude_mpm/__main__.py +3 -2
  4. claude_mpm/agents/__init__.py +85 -79
  5. claude_mpm/agents/agent_loader.py +464 -1003
  6. claude_mpm/agents/agent_loader_integration.py +45 -45
  7. claude_mpm/agents/agents_metadata.py +29 -30
  8. claude_mpm/agents/async_agent_loader.py +156 -138
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/base_agent_loader.py +179 -151
  11. claude_mpm/agents/frontmatter_validator.py +229 -130
  12. claude_mpm/agents/schema/agent_schema.json +1 -1
  13. claude_mpm/agents/system_agent_config.py +213 -147
  14. claude_mpm/agents/templates/__init__.py +13 -13
  15. claude_mpm/agents/templates/code_analyzer.json +2 -2
  16. claude_mpm/agents/templates/data_engineer.json +1 -1
  17. claude_mpm/agents/templates/documentation.json +23 -11
  18. claude_mpm/agents/templates/engineer.json +22 -6
  19. claude_mpm/agents/templates/memory_manager.json +1 -1
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/project_organizer.json +1 -1
  22. claude_mpm/agents/templates/qa.json +1 -1
  23. claude_mpm/agents/templates/refactoring_engineer.json +222 -0
  24. claude_mpm/agents/templates/research.json +20 -14
  25. claude_mpm/agents/templates/security.json +1 -1
  26. claude_mpm/agents/templates/ticketing.json +1 -1
  27. claude_mpm/agents/templates/version_control.json +1 -1
  28. claude_mpm/agents/templates/web_qa.json +3 -1
  29. claude_mpm/agents/templates/web_ui.json +2 -2
  30. claude_mpm/cli/__init__.py +79 -51
  31. claude_mpm/cli/__main__.py +3 -2
  32. claude_mpm/cli/commands/__init__.py +20 -20
  33. claude_mpm/cli/commands/agents.py +279 -247
  34. claude_mpm/cli/commands/aggregate.py +138 -157
  35. claude_mpm/cli/commands/cleanup.py +147 -147
  36. claude_mpm/cli/commands/config.py +93 -76
  37. claude_mpm/cli/commands/info.py +17 -16
  38. claude_mpm/cli/commands/mcp.py +140 -905
  39. claude_mpm/cli/commands/mcp_command_router.py +139 -0
  40. claude_mpm/cli/commands/mcp_config_commands.py +20 -0
  41. claude_mpm/cli/commands/mcp_install_commands.py +20 -0
  42. claude_mpm/cli/commands/mcp_server_commands.py +175 -0
  43. claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
  44. claude_mpm/cli/commands/memory.py +239 -203
  45. claude_mpm/cli/commands/monitor.py +203 -81
  46. claude_mpm/cli/commands/run.py +380 -429
  47. claude_mpm/cli/commands/run_config_checker.py +160 -0
  48. claude_mpm/cli/commands/socketio_monitor.py +235 -0
  49. claude_mpm/cli/commands/tickets.py +305 -197
  50. claude_mpm/cli/parser.py +24 -1156
  51. claude_mpm/cli/parsers/__init__.py +29 -0
  52. claude_mpm/cli/parsers/agents_parser.py +136 -0
  53. claude_mpm/cli/parsers/base_parser.py +331 -0
  54. claude_mpm/cli/parsers/config_parser.py +85 -0
  55. claude_mpm/cli/parsers/mcp_parser.py +152 -0
  56. claude_mpm/cli/parsers/memory_parser.py +138 -0
  57. claude_mpm/cli/parsers/monitor_parser.py +104 -0
  58. claude_mpm/cli/parsers/run_parser.py +147 -0
  59. claude_mpm/cli/parsers/tickets_parser.py +203 -0
  60. claude_mpm/cli/ticket_cli.py +7 -3
  61. claude_mpm/cli/utils.py +55 -37
  62. claude_mpm/cli_module/__init__.py +6 -6
  63. claude_mpm/cli_module/args.py +188 -140
  64. claude_mpm/cli_module/commands.py +79 -70
  65. claude_mpm/cli_module/migration_example.py +38 -60
  66. claude_mpm/config/__init__.py +32 -25
  67. claude_mpm/config/agent_config.py +151 -119
  68. claude_mpm/config/experimental_features.py +71 -73
  69. claude_mpm/config/paths.py +94 -208
  70. claude_mpm/config/socketio_config.py +84 -73
  71. claude_mpm/constants.py +35 -18
  72. claude_mpm/core/__init__.py +9 -6
  73. claude_mpm/core/agent_name_normalizer.py +68 -71
  74. claude_mpm/core/agent_registry.py +372 -521
  75. claude_mpm/core/agent_session_manager.py +74 -63
  76. claude_mpm/core/base_service.py +116 -87
  77. claude_mpm/core/cache.py +119 -153
  78. claude_mpm/core/claude_runner.py +425 -1120
  79. claude_mpm/core/config.py +263 -168
  80. claude_mpm/core/config_aliases.py +69 -61
  81. claude_mpm/core/config_constants.py +292 -0
  82. claude_mpm/core/constants.py +57 -99
  83. claude_mpm/core/container.py +211 -178
  84. claude_mpm/core/exceptions.py +233 -89
  85. claude_mpm/core/factories.py +92 -54
  86. claude_mpm/core/framework_loader.py +378 -220
  87. claude_mpm/core/hook_manager.py +198 -83
  88. claude_mpm/core/hook_performance_config.py +136 -0
  89. claude_mpm/core/injectable_service.py +61 -55
  90. claude_mpm/core/interactive_session.py +165 -155
  91. claude_mpm/core/interfaces.py +221 -195
  92. claude_mpm/core/lazy.py +96 -96
  93. claude_mpm/core/logger.py +133 -107
  94. claude_mpm/core/logging_config.py +185 -157
  95. claude_mpm/core/minimal_framework_loader.py +20 -15
  96. claude_mpm/core/mixins.py +30 -29
  97. claude_mpm/core/oneshot_session.py +215 -181
  98. claude_mpm/core/optimized_agent_loader.py +134 -138
  99. claude_mpm/core/optimized_startup.py +159 -157
  100. claude_mpm/core/pm_hook_interceptor.py +85 -72
  101. claude_mpm/core/service_registry.py +103 -101
  102. claude_mpm/core/session_manager.py +97 -87
  103. claude_mpm/core/socketio_pool.py +212 -158
  104. claude_mpm/core/tool_access_control.py +58 -51
  105. claude_mpm/core/types.py +46 -24
  106. claude_mpm/core/typing_utils.py +166 -82
  107. claude_mpm/core/unified_agent_registry.py +721 -0
  108. claude_mpm/core/unified_config.py +550 -0
  109. claude_mpm/core/unified_paths.py +549 -0
  110. claude_mpm/dashboard/index.html +1 -1
  111. claude_mpm/dashboard/open_dashboard.py +51 -17
  112. claude_mpm/dashboard/static/css/dashboard.css +27 -8
  113. claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
  114. claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
  115. claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
  116. claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
  117. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
  118. claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
  119. claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
  120. claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
  121. claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
  122. claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
  123. claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
  124. claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
  125. claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
  126. claude_mpm/dashboard/static/dist/dashboard.js +2 -0
  127. claude_mpm/dashboard/static/dist/socket-client.js +2 -0
  128. claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
  129. claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
  130. claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
  131. claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
  132. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
  133. claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
  134. claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
  135. claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
  136. claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
  137. claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
  138. claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
  139. claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
  140. claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
  141. claude_mpm/dashboard/static/js/dashboard.js +178 -453
  142. claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
  143. claude_mpm/dashboard/static/js/socket-client.js +120 -54
  144. claude_mpm/dashboard/templates/index.html +40 -50
  145. claude_mpm/experimental/cli_enhancements.py +60 -58
  146. claude_mpm/generators/__init__.py +1 -1
  147. claude_mpm/generators/agent_profile_generator.py +75 -65
  148. claude_mpm/hooks/__init__.py +1 -1
  149. claude_mpm/hooks/base_hook.py +33 -28
  150. claude_mpm/hooks/claude_hooks/__init__.py +1 -1
  151. claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
  152. claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
  153. claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
  154. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
  155. claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
  156. claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
  157. claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
  158. claude_mpm/hooks/memory_integration_hook.py +140 -100
  159. claude_mpm/hooks/tool_call_interceptor.py +89 -76
  160. claude_mpm/hooks/validation_hooks.py +57 -49
  161. claude_mpm/init.py +145 -121
  162. claude_mpm/models/__init__.py +9 -9
  163. claude_mpm/models/agent_definition.py +33 -23
  164. claude_mpm/models/agent_session.py +228 -200
  165. claude_mpm/scripts/__init__.py +1 -1
  166. claude_mpm/scripts/socketio_daemon.py +192 -75
  167. claude_mpm/scripts/socketio_server_manager.py +328 -0
  168. claude_mpm/scripts/start_activity_logging.py +25 -22
  169. claude_mpm/services/__init__.py +68 -43
  170. claude_mpm/services/agent_capabilities_service.py +271 -0
  171. claude_mpm/services/agents/__init__.py +23 -32
  172. claude_mpm/services/agents/deployment/__init__.py +3 -3
  173. claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
  174. claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
  175. claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
  176. claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
  177. claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
  178. claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
  179. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
  180. claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
  181. claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
  182. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
  183. claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
  184. claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
  185. claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
  186. claude_mpm/services/agents/deployment/agent_validator.py +352 -0
  187. claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
  188. claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
  189. claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
  190. claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
  191. claude_mpm/services/agents/deployment/config/__init__.py +13 -0
  192. claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
  193. claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
  194. claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
  195. claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
  196. claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
  197. claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
  198. claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
  199. claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
  200. claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
  201. claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
  202. claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
  203. claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
  204. claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
  205. claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
  206. claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
  207. claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
  208. claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
  209. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
  210. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
  211. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
  212. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
  213. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
  214. claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
  215. claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
  216. claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
  217. claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
  218. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
  219. claude_mpm/services/agents/deployment/results/__init__.py +13 -0
  220. claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
  221. claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
  222. claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
  223. claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
  224. claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
  225. claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
  226. claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
  227. claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
  228. claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
  229. claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
  230. claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
  231. claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
  232. claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
  233. claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
  234. claude_mpm/services/agents/loading/__init__.py +2 -2
  235. claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
  236. claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
  237. claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
  238. claude_mpm/services/agents/management/__init__.py +2 -2
  239. claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
  240. claude_mpm/services/agents/management/agent_management_service.py +209 -156
  241. claude_mpm/services/agents/memory/__init__.py +9 -6
  242. claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
  243. claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
  244. claude_mpm/services/agents/memory/analyzer.py +430 -0
  245. claude_mpm/services/agents/memory/content_manager.py +376 -0
  246. claude_mpm/services/agents/memory/template_generator.py +468 -0
  247. claude_mpm/services/agents/registry/__init__.py +7 -10
  248. claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
  249. claude_mpm/services/agents/registry/modification_tracker.py +351 -285
  250. claude_mpm/services/async_session_logger.py +187 -153
  251. claude_mpm/services/claude_session_logger.py +87 -72
  252. claude_mpm/services/command_handler_service.py +217 -0
  253. claude_mpm/services/communication/__init__.py +3 -2
  254. claude_mpm/services/core/__init__.py +50 -97
  255. claude_mpm/services/core/base.py +60 -53
  256. claude_mpm/services/core/interfaces/__init__.py +188 -0
  257. claude_mpm/services/core/interfaces/agent.py +351 -0
  258. claude_mpm/services/core/interfaces/communication.py +343 -0
  259. claude_mpm/services/core/interfaces/infrastructure.py +413 -0
  260. claude_mpm/services/core/interfaces/service.py +434 -0
  261. claude_mpm/services/core/interfaces.py +19 -944
  262. claude_mpm/services/event_aggregator.py +208 -170
  263. claude_mpm/services/exceptions.py +387 -308
  264. claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
  265. claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
  266. claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
  267. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
  268. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
  269. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
  270. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
  271. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
  272. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
  273. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
  274. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
  275. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
  276. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
  277. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
  278. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
  279. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
  280. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
  281. claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
  282. claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
  283. claude_mpm/services/hook_service.py +106 -114
  284. claude_mpm/services/infrastructure/__init__.py +7 -5
  285. claude_mpm/services/infrastructure/context_preservation.py +233 -199
  286. claude_mpm/services/infrastructure/daemon_manager.py +279 -0
  287. claude_mpm/services/infrastructure/logging.py +83 -76
  288. claude_mpm/services/infrastructure/monitoring.py +547 -404
  289. claude_mpm/services/mcp_gateway/__init__.py +30 -13
  290. claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
  291. claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
  292. claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
  293. claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
  294. claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
  295. claude_mpm/services/mcp_gateway/core/base.py +80 -67
  296. claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
  297. claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
  298. claude_mpm/services/mcp_gateway/main.py +287 -137
  299. claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
  300. claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
  301. claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
  302. claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
  303. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
  304. claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
  305. claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
  306. claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
  307. claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
  308. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
  309. claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
  310. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
  311. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
  312. claude_mpm/services/memory/__init__.py +2 -2
  313. claude_mpm/services/memory/builder.py +451 -362
  314. claude_mpm/services/memory/cache/__init__.py +2 -2
  315. claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
  316. claude_mpm/services/memory/cache/simple_cache.py +107 -93
  317. claude_mpm/services/memory/indexed_memory.py +195 -193
  318. claude_mpm/services/memory/optimizer.py +267 -234
  319. claude_mpm/services/memory/router.py +571 -263
  320. claude_mpm/services/memory_hook_service.py +237 -0
  321. claude_mpm/services/port_manager.py +223 -0
  322. claude_mpm/services/project/__init__.py +3 -3
  323. claude_mpm/services/project/analyzer.py +451 -305
  324. claude_mpm/services/project/registry.py +262 -240
  325. claude_mpm/services/recovery_manager.py +287 -231
  326. claude_mpm/services/response_tracker.py +87 -67
  327. claude_mpm/services/runner_configuration_service.py +587 -0
  328. claude_mpm/services/session_management_service.py +304 -0
  329. claude_mpm/services/socketio/__init__.py +4 -4
  330. claude_mpm/services/socketio/client_proxy.py +174 -0
  331. claude_mpm/services/socketio/handlers/__init__.py +3 -3
  332. claude_mpm/services/socketio/handlers/base.py +44 -30
  333. claude_mpm/services/socketio/handlers/connection.py +145 -65
  334. claude_mpm/services/socketio/handlers/file.py +123 -108
  335. claude_mpm/services/socketio/handlers/git.py +607 -373
  336. claude_mpm/services/socketio/handlers/hook.py +170 -0
  337. claude_mpm/services/socketio/handlers/memory.py +4 -4
  338. claude_mpm/services/socketio/handlers/project.py +4 -4
  339. claude_mpm/services/socketio/handlers/registry.py +53 -38
  340. claude_mpm/services/socketio/server/__init__.py +18 -0
  341. claude_mpm/services/socketio/server/broadcaster.py +252 -0
  342. claude_mpm/services/socketio/server/core.py +399 -0
  343. claude_mpm/services/socketio/server/main.py +323 -0
  344. claude_mpm/services/socketio_client_manager.py +160 -133
  345. claude_mpm/services/socketio_server.py +36 -1885
  346. claude_mpm/services/subprocess_launcher_service.py +316 -0
  347. claude_mpm/services/system_instructions_service.py +258 -0
  348. claude_mpm/services/ticket_manager.py +19 -533
  349. claude_mpm/services/utility_service.py +285 -0
  350. claude_mpm/services/version_control/__init__.py +18 -21
  351. claude_mpm/services/version_control/branch_strategy.py +20 -10
  352. claude_mpm/services/version_control/conflict_resolution.py +37 -13
  353. claude_mpm/services/version_control/git_operations.py +52 -21
  354. claude_mpm/services/version_control/semantic_versioning.py +92 -53
  355. claude_mpm/services/version_control/version_parser.py +145 -125
  356. claude_mpm/services/version_service.py +270 -0
  357. claude_mpm/storage/__init__.py +2 -2
  358. claude_mpm/storage/state_storage.py +177 -181
  359. claude_mpm/ticket_wrapper.py +2 -2
  360. claude_mpm/utils/__init__.py +2 -2
  361. claude_mpm/utils/agent_dependency_loader.py +453 -243
  362. claude_mpm/utils/config_manager.py +157 -118
  363. claude_mpm/utils/console.py +1 -1
  364. claude_mpm/utils/dependency_cache.py +102 -107
  365. claude_mpm/utils/dependency_manager.py +52 -47
  366. claude_mpm/utils/dependency_strategies.py +131 -96
  367. claude_mpm/utils/environment_context.py +110 -102
  368. claude_mpm/utils/error_handler.py +75 -55
  369. claude_mpm/utils/file_utils.py +80 -67
  370. claude_mpm/utils/framework_detection.py +12 -11
  371. claude_mpm/utils/import_migration_example.py +12 -60
  372. claude_mpm/utils/imports.py +48 -45
  373. claude_mpm/utils/path_operations.py +100 -93
  374. claude_mpm/utils/robust_installer.py +172 -164
  375. claude_mpm/utils/session_logging.py +30 -23
  376. claude_mpm/utils/subprocess_utils.py +99 -61
  377. claude_mpm/validation/__init__.py +1 -1
  378. claude_mpm/validation/agent_validator.py +151 -111
  379. claude_mpm/validation/frontmatter_validator.py +92 -71
  380. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
  381. claude_mpm-4.0.3.dist-info/RECORD +402 -0
  382. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
  383. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
  384. claude_mpm/cli/commands/run_guarded.py +0 -511
  385. claude_mpm/config/memory_guardian_config.py +0 -325
  386. claude_mpm/config/memory_guardian_yaml.py +0 -335
  387. claude_mpm/core/config_paths.py +0 -150
  388. claude_mpm/core/memory_aware_runner.py +0 -353
  389. claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
  390. claude_mpm/deployment_paths.py +0 -261
  391. claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
  392. claude_mpm/models/state_models.py +0 -433
  393. claude_mpm/services/agent/__init__.py +0 -24
  394. claude_mpm/services/agent/deployment.py +0 -2548
  395. claude_mpm/services/agent/management.py +0 -598
  396. claude_mpm/services/agent/registry.py +0 -813
  397. claude_mpm/services/agents/registry/agent_registry.py +0 -813
  398. claude_mpm/services/communication/socketio.py +0 -1935
  399. claude_mpm/services/communication/websocket.py +0 -479
  400. claude_mpm/services/framework_claude_md_generator.py +0 -624
  401. claude_mpm/services/health_monitor.py +0 -893
  402. claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
  403. claude_mpm/services/infrastructure/health_monitor.py +0 -775
  404. claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
  405. claude_mpm/services/infrastructure/memory_guardian.py +0 -944
  406. claude_mpm/services/infrastructure/restart_protection.py +0 -642
  407. claude_mpm/services/infrastructure/state_manager.py +0 -774
  408. claude_mpm/services/mcp_gateway/manager.py +0 -334
  409. claude_mpm/services/optimized_hook_service.py +0 -542
  410. claude_mpm/services/project_analyzer.py +0 -864
  411. claude_mpm/services/project_registry.py +0 -608
  412. claude_mpm/services/standalone_socketio_server.py +0 -1300
  413. claude_mpm/services/ticket_manager_di.py +0 -318
  414. claude_mpm/services/ticketing_service_original.py +0 -510
  415. claude_mpm/utils/paths.py +0 -395
  416. claude_mpm/utils/platform_memory.py +0 -524
  417. claude_mpm-3.9.11.dist-info/RECORD +0 -306
  418. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
  419. {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,5 @@
1
+ from pathlib import Path
2
+
1
3
  #!/usr/bin/env python3
2
4
  """Indexed memory service for high-performance memory queries.
3
5
 
@@ -16,25 +18,24 @@ WHY indexed memory:
16
18
 
17
19
  import bisect
18
20
  import hashlib
19
- import json
20
21
  import mmap
21
22
  import os
22
23
  import pickle
23
24
  import re
24
25
  import time
25
- from collections import defaultdict, Counter
26
+ from collections import Counter, defaultdict
26
27
  from dataclasses import dataclass, field
27
28
  from datetime import datetime, timedelta
28
- from pathlib import Path
29
29
  from typing import Any, Dict, List, Optional, Set, Tuple, Union
30
30
 
31
- from ...core.logger import get_logger
32
31
  from ...core.cache import FileSystemCache, get_file_cache
32
+ from ...core.logger import get_logger
33
33
 
34
34
 
35
35
  @dataclass
36
36
  class MemoryEntry:
37
37
  """Single memory entry with metadata."""
38
+
38
39
  id: str
39
40
  agent_id: str
40
41
  content: str
@@ -48,6 +49,7 @@ class MemoryEntry:
48
49
  @dataclass
49
50
  class QueryResult:
50
51
  """Result from a memory query."""
52
+
51
53
  entries: List[MemoryEntry]
52
54
  total_count: int
53
55
  query_time: float
@@ -57,14 +59,14 @@ class QueryResult:
57
59
 
58
60
  class InvertedIndex:
59
61
  """Inverted index for fast text searches.
60
-
62
+
61
63
  WHY inverted index:
62
64
  - Maps words to document IDs for O(1) lookups
63
65
  - Supports boolean queries (AND, OR, NOT)
64
66
  - Enables relevance scoring with TF-IDF
65
67
  - Efficient for full-text search
66
68
  """
67
-
69
+
68
70
  def __init__(self):
69
71
  # Word -> Set of memory IDs
70
72
  self.index: Dict[str, Set[str]] = defaultdict(set)
@@ -72,73 +74,69 @@ class InvertedIndex:
72
74
  self.doc_freqs: Dict[str, Counter] = {}
73
75
  # Total documents
74
76
  self.doc_count = 0
75
-
77
+
76
78
  self.logger = get_logger("inverted_index")
77
-
79
+
78
80
  def add_entry(self, entry_id: str, text: str):
79
81
  """Add entry to inverted index."""
80
82
  # Tokenize text
81
83
  words = self._tokenize(text)
82
-
84
+
83
85
  # Update word frequencies
84
86
  self.doc_freqs[entry_id] = Counter(words)
85
-
87
+
86
88
  # Update inverted index
87
89
  for word in set(words):
88
90
  self.index[word].add(entry_id)
89
-
91
+
90
92
  self.doc_count += 1
91
-
93
+
92
94
  def remove_entry(self, entry_id: str):
93
95
  """Remove entry from index."""
94
96
  if entry_id not in self.doc_freqs:
95
97
  return
96
-
98
+
97
99
  # Remove from inverted index
98
100
  words = self.doc_freqs[entry_id].keys()
99
101
  for word in words:
100
102
  self.index[word].discard(entry_id)
101
103
  if not self.index[word]:
102
104
  del self.index[word]
103
-
105
+
104
106
  # Remove document frequency
105
107
  del self.doc_freqs[entry_id]
106
108
  self.doc_count -= 1
107
-
108
- def search(
109
- self,
110
- query: str,
111
- operator: str = 'AND'
112
- ) -> Set[str]:
109
+
110
+ def search(self, query: str, operator: str = "AND") -> Set[str]:
113
111
  """Search index for matching entries.
114
-
112
+
115
113
  Args:
116
114
  query: Search query
117
115
  operator: Boolean operator (AND, OR, NOT)
118
-
116
+
119
117
  Returns:
120
118
  Set of matching entry IDs
121
119
  """
122
120
  words = self._tokenize(query)
123
121
  if not words:
124
122
  return set()
125
-
123
+
126
124
  # Get entry sets for each word
127
125
  entry_sets = [self.index.get(word, set()) for word in words]
128
-
126
+
129
127
  if not entry_sets:
130
128
  return set()
131
-
129
+
132
130
  # Apply boolean operator
133
- if operator == 'AND':
131
+ if operator == "AND":
134
132
  result = entry_sets[0]
135
133
  for s in entry_sets[1:]:
136
134
  result = result.intersection(s)
137
- elif operator == 'OR':
135
+ elif operator == "OR":
138
136
  result = set()
139
137
  for s in entry_sets:
140
138
  result = result.union(s)
141
- elif operator == 'NOT':
139
+ elif operator == "NOT":
142
140
  # Return entries that don't contain any query words
143
141
  all_entries = set(self.doc_freqs.keys())
144
142
  excluded = set()
@@ -147,149 +145,154 @@ class InvertedIndex:
147
145
  result = all_entries - excluded
148
146
  else:
149
147
  result = entry_sets[0]
150
-
148
+
151
149
  return result
152
-
153
- def calculate_relevance(
154
- self,
155
- entry_id: str,
156
- query: str
157
- ) -> float:
150
+
151
+ def calculate_relevance(self, entry_id: str, query: str) -> float:
158
152
  """Calculate TF-IDF relevance score.
159
-
153
+
160
154
  Args:
161
155
  entry_id: Memory entry ID
162
156
  query: Search query
163
-
157
+
164
158
  Returns:
165
159
  Relevance score (0.0 to 1.0)
166
160
  """
167
161
  if entry_id not in self.doc_freqs:
168
162
  return 0.0
169
-
163
+
170
164
  query_words = self._tokenize(query)
171
165
  if not query_words:
172
166
  return 0.0
173
-
167
+
174
168
  score = 0.0
175
169
  doc_freq = self.doc_freqs[entry_id]
176
-
170
+
177
171
  for word in query_words:
178
172
  if word not in doc_freq:
179
173
  continue
180
-
174
+
181
175
  # Term frequency
182
176
  tf = doc_freq[word] / sum(doc_freq.values())
183
-
177
+
184
178
  # Inverse document frequency
185
179
  if word in self.index:
186
180
  idf = 1.0 + (self.doc_count / len(self.index[word]))
187
181
  else:
188
182
  idf = 1.0
189
-
183
+
190
184
  score += tf * idf
191
-
185
+
192
186
  # Normalize score
193
187
  return min(1.0, score / len(query_words))
194
-
188
+
195
189
  def _tokenize(self, text: str) -> List[str]:
196
190
  """Tokenize text into words."""
197
191
  # Convert to lowercase and split on non-alphanumeric
198
192
  text = text.lower()
199
- words = re.findall(r'\b[a-z0-9]+\b', text)
200
-
193
+ words = re.findall(r"\b[a-z0-9]+\b", text)
194
+
201
195
  # Remove stop words (simplified list)
202
- stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for'}
196
+ stop_words = {
197
+ "the",
198
+ "a",
199
+ "an",
200
+ "and",
201
+ "or",
202
+ "but",
203
+ "in",
204
+ "on",
205
+ "at",
206
+ "to",
207
+ "for",
208
+ }
203
209
  return [w for w in words if w not in stop_words and len(w) > 2]
204
-
210
+
205
211
  def save(self, path: Path):
206
212
  """Persist index to disk."""
207
213
  data = {
208
- 'index': dict(self.index),
209
- 'doc_freqs': dict(self.doc_freqs),
210
- 'doc_count': self.doc_count
214
+ "index": dict(self.index),
215
+ "doc_freqs": dict(self.doc_freqs),
216
+ "doc_count": self.doc_count,
211
217
  }
212
- with open(path, 'wb') as f:
218
+ with open(path, "wb") as f:
213
219
  pickle.dump(data, f)
214
-
220
+
215
221
  def load(self, path: Path):
216
222
  """Load index from disk."""
217
223
  if not path.exists():
218
224
  return
219
-
220
- with open(path, 'rb') as f:
225
+
226
+ with open(path, "rb") as f:
221
227
  data = pickle.load(f)
222
-
223
- self.index = defaultdict(set, {k: set(v) for k, v in data['index'].items()})
224
- self.doc_freqs = data['doc_freqs']
225
- self.doc_count = data['doc_count']
228
+
229
+ self.index = defaultdict(set, {k: set(v) for k, v in data["index"].items()})
230
+ self.doc_freqs = data["doc_freqs"]
231
+ self.doc_count = data["doc_count"]
226
232
 
227
233
 
228
234
  class BTreeIndex:
229
235
  """B-tree index for sorted queries.
230
-
236
+
231
237
  WHY B-tree index:
232
238
  - Maintains sorted order for range queries
233
239
  - O(log n) search, insert, delete
234
240
  - Efficient for timestamp-based queries
235
241
  - Supports pagination
236
242
  """
237
-
243
+
238
244
  def __init__(self, key_func=None):
239
245
  # Sorted list of (key, entry_id) tuples
240
246
  self.index: List[Tuple[Any, str]] = []
241
247
  self.key_func = key_func or (lambda x: x)
242
248
  self.logger = get_logger("btree_index")
243
-
249
+
244
250
  def add_entry(self, entry_id: str, key: Any):
245
251
  """Add entry to B-tree index."""
246
252
  bisect.insort(self.index, (self.key_func(key), entry_id))
247
-
253
+
248
254
  def remove_entry(self, entry_id: str):
249
255
  """Remove entry from index."""
250
256
  self.index = [(k, id) for k, id in self.index if id != entry_id]
251
-
257
+
252
258
  def range_search(
253
- self,
254
- min_key: Any = None,
255
- max_key: Any = None,
256
- limit: int = None
259
+ self, min_key: Any = None, max_key: Any = None, limit: int = None
257
260
  ) -> List[str]:
258
261
  """Search for entries in key range.
259
-
262
+
260
263
  Args:
261
264
  min_key: Minimum key value (inclusive)
262
265
  max_key: Maximum key value (inclusive)
263
266
  limit: Maximum results to return
264
-
267
+
265
268
  Returns:
266
269
  List of matching entry IDs
267
270
  """
268
271
  # Find range boundaries
269
272
  if min_key is not None:
270
273
  min_key = self.key_func(min_key)
271
- start = bisect.bisect_left(self.index, (min_key, ''))
274
+ start = bisect.bisect_left(self.index, (min_key, ""))
272
275
  else:
273
276
  start = 0
274
-
277
+
275
278
  if max_key is not None:
276
279
  max_key = self.key_func(max_key)
277
- end = bisect.bisect_right(self.index, (max_key, '\xff'))
280
+ end = bisect.bisect_right(self.index, (max_key, "\xff"))
278
281
  else:
279
282
  end = len(self.index)
280
-
283
+
281
284
  # Extract entry IDs
282
285
  results = [entry_id for _, entry_id in self.index[start:end]]
283
-
286
+
284
287
  if limit:
285
288
  results = results[:limit]
286
-
289
+
287
290
  return results
288
-
291
+
289
292
  def get_recent(self, n: int = 10) -> List[str]:
290
293
  """Get n most recent entries."""
291
294
  return [entry_id for _, entry_id in self.index[-n:]]
292
-
295
+
293
296
  def get_oldest(self, n: int = 10) -> List[str]:
294
297
  """Get n oldest entries."""
295
298
  return [entry_id for _, entry_id in self.index[:n]]
@@ -297,38 +300,38 @@ class BTreeIndex:
297
300
 
298
301
  class IndexedMemoryService:
299
302
  """High-performance memory service with multiple indexes.
300
-
303
+
301
304
  WHY this design:
302
305
  - Multiple specialized indexes for different query types
303
306
  - LRU cache for frequent queries
304
307
  - Incremental index updates for efficiency
305
308
  - Memory-mapped files for large datasets
306
-
309
+
307
310
  Example:
308
311
  memory = IndexedMemoryService()
309
-
312
+
310
313
  # Add memory entry
311
314
  memory.add_memory(
312
315
  agent_id='engineer',
313
316
  content='Use dependency injection for testability',
314
317
  category='pattern'
315
318
  )
316
-
319
+
317
320
  # Fast text search
318
321
  results = memory.search('dependency injection')
319
-
322
+
320
323
  # Range query by timestamp
321
324
  recent = memory.get_recent_memories(hours=24)
322
325
  """
323
-
326
+
324
327
  def __init__(
325
328
  self,
326
329
  data_dir: Optional[Path] = None,
327
330
  cache_size_mb: int = 50,
328
- enable_mmap: bool = False
331
+ enable_mmap: bool = False,
329
332
  ):
330
333
  """Initialize indexed memory service.
331
-
334
+
332
335
  Args:
333
336
  data_dir: Directory for persisting indexes
334
337
  cache_size_mb: Cache size for query results
@@ -336,51 +339,51 @@ class IndexedMemoryService:
336
339
  """
337
340
  self.data_dir = data_dir or Path.home() / ".claude-mpm" / "memory"
338
341
  self.data_dir.mkdir(parents=True, exist_ok=True)
339
-
342
+
340
343
  self.enable_mmap = enable_mmap
341
-
344
+
342
345
  # Memory storage
343
346
  self.memories: Dict[str, MemoryEntry] = {}
344
-
347
+
345
348
  # Indexes
346
349
  self.text_index = InvertedIndex()
347
350
  self.time_index = BTreeIndex(key_func=lambda dt: dt.timestamp())
348
351
  self.agent_index: Dict[str, Set[str]] = defaultdict(set)
349
352
  self.category_index: Dict[str, Set[str]] = defaultdict(set)
350
353
  self.tag_index: Dict[str, Set[str]] = defaultdict(set)
351
-
354
+
352
355
  # Query cache
353
356
  self.cache = get_file_cache(max_size_mb=cache_size_mb, default_ttl=300)
354
-
357
+
355
358
  # Logger
356
359
  self.logger = get_logger("indexed_memory")
357
-
360
+
358
361
  # Load existing data
359
362
  self._load_indexes()
360
-
363
+
361
364
  def add_memory(
362
365
  self,
363
366
  agent_id: str,
364
367
  content: str,
365
- category: str = 'general',
368
+ category: str = "general",
366
369
  tags: List[str] = None,
367
- metadata: Dict[str, Any] = None
370
+ metadata: Dict[str, Any] = None,
368
371
  ) -> str:
369
372
  """Add a new memory entry.
370
-
373
+
371
374
  Args:
372
375
  agent_id: Agent that owns this memory
373
376
  content: Memory content
374
377
  category: Memory category
375
378
  tags: Optional tags
376
379
  metadata: Optional metadata
377
-
380
+
378
381
  Returns:
379
382
  Memory entry ID
380
383
  """
381
384
  # Generate ID
382
385
  entry_id = self._generate_id(agent_id, content)
383
-
386
+
384
387
  # Create entry
385
388
  entry = MemoryEntry(
386
389
  id=entry_id,
@@ -389,12 +392,12 @@ class IndexedMemoryService:
389
392
  category=category,
390
393
  timestamp=datetime.now(),
391
394
  tags=tags or [],
392
- metadata=metadata or {}
395
+ metadata=metadata or {},
393
396
  )
394
-
397
+
395
398
  # Store entry
396
399
  self.memories[entry_id] = entry
397
-
400
+
398
401
  # Update indexes
399
402
  self.text_index.add_entry(entry_id, content)
400
403
  self.time_index.add_entry(entry_id, entry.timestamp)
@@ -402,13 +405,13 @@ class IndexedMemoryService:
402
405
  self.category_index[category].add(entry_id)
403
406
  for tag in entry.tags:
404
407
  self.tag_index[tag].add(entry_id)
405
-
408
+
406
409
  # Invalidate cache
407
410
  self.cache.invalidate_pattern("query:*")
408
-
411
+
409
412
  self.logger.debug(f"Added memory {entry_id} for agent {agent_id}")
410
413
  return entry_id
411
-
414
+
412
415
  def search(
413
416
  self,
414
417
  query: str,
@@ -416,10 +419,10 @@ class IndexedMemoryService:
416
419
  category: Optional[str] = None,
417
420
  tags: Optional[List[str]] = None,
418
421
  limit: int = 50,
419
- operator: str = 'AND'
422
+ operator: str = "AND",
420
423
  ) -> QueryResult:
421
424
  """Search memories with multiple filters.
422
-
425
+
423
426
  Args:
424
427
  query: Text search query
425
428
  agent_id: Filter by agent
@@ -427,91 +430,87 @@ class IndexedMemoryService:
427
430
  tags: Filter by tags
428
431
  limit: Maximum results
429
432
  operator: Boolean operator for text search
430
-
433
+
431
434
  Returns:
432
435
  Query results with metadata
433
436
  """
434
437
  start_time = time.time()
435
-
438
+
436
439
  # Generate cache key
437
440
  cache_key = f"query:{hashlib.md5(f'{query}:{agent_id}:{category}:{tags}:{limit}:{operator}'.encode()).hexdigest()}"
438
-
441
+
439
442
  # Check cache
440
443
  cached = self.cache.get(cache_key)
441
444
  if cached:
442
445
  return QueryResult(
443
- entries=cached['entries'],
444
- total_count=cached['total_count'],
446
+ entries=cached["entries"],
447
+ total_count=cached["total_count"],
445
448
  query_time=0.001,
446
- index_used='cache',
447
- cache_hit=True
449
+ index_used="cache",
450
+ cache_hit=True,
448
451
  )
449
-
452
+
450
453
  # Text search
451
454
  if query:
452
455
  matching_ids = self.text_index.search(query, operator)
453
456
  else:
454
457
  matching_ids = set(self.memories.keys())
455
-
458
+
456
459
  # Apply filters
457
460
  if agent_id:
458
461
  matching_ids &= self.agent_index.get(agent_id, set())
459
-
462
+
460
463
  if category:
461
464
  matching_ids &= self.category_index.get(category, set())
462
-
465
+
463
466
  if tags:
464
467
  for tag in tags:
465
468
  matching_ids &= self.tag_index.get(tag, set())
466
-
469
+
467
470
  # Get entries and calculate relevance
468
471
  entries = []
469
472
  for entry_id in matching_ids:
470
473
  if entry_id in self.memories:
471
474
  entry = self.memories[entry_id]
472
475
  if query:
473
- entry.relevance_score = self.text_index.calculate_relevance(entry_id, query)
476
+ entry.relevance_score = self.text_index.calculate_relevance(
477
+ entry_id, query
478
+ )
474
479
  entries.append(entry)
475
-
480
+
476
481
  # Sort by relevance and timestamp
477
482
  entries.sort(key=lambda e: (-e.relevance_score, -e.timestamp.timestamp()))
478
-
483
+
479
484
  # Apply limit
480
485
  limited_entries = entries[:limit]
481
-
486
+
482
487
  # Cache result
483
- cache_data = {
484
- 'entries': limited_entries,
485
- 'total_count': len(entries)
486
- }
488
+ cache_data = {"entries": limited_entries, "total_count": len(entries)}
487
489
  self.cache.put(cache_key, cache_data, ttl=300)
488
-
490
+
489
491
  # Return result
490
492
  return QueryResult(
491
493
  entries=limited_entries,
492
494
  total_count=len(entries),
493
495
  query_time=time.time() - start_time,
494
- index_used='text_index' if query else 'full_scan'
496
+ index_used="text_index" if query else "full_scan",
495
497
  )
496
-
498
+
497
499
  def get_recent_memories(
498
- self,
499
- hours: Optional[int] = None,
500
- days: Optional[int] = None,
501
- limit: int = 50
500
+ self, hours: Optional[int] = None, days: Optional[int] = None, limit: int = 50
502
501
  ) -> QueryResult:
503
502
  """Get recent memories within time range.
504
-
503
+
505
504
  Args:
506
505
  hours: Hours to look back
507
506
  days: Days to look back
508
507
  limit: Maximum results
509
-
508
+
510
509
  Returns:
511
510
  Recent memories
512
511
  """
513
512
  start_time = time.time()
514
-
513
+
515
514
  # Calculate time range
516
515
  now = datetime.now()
517
516
  if hours:
@@ -520,117 +519,120 @@ class IndexedMemoryService:
520
519
  min_time = now - timedelta(days=days)
521
520
  else:
522
521
  min_time = None
523
-
522
+
524
523
  # Use time index for range search
525
524
  entry_ids = self.time_index.range_search(
526
- min_key=min_time,
527
- max_key=now,
528
- limit=limit
525
+ min_key=min_time, max_key=now, limit=limit
529
526
  )
530
-
527
+
531
528
  # Get entries
532
529
  entries = [self.memories[id] for id in entry_ids if id in self.memories]
533
-
530
+
534
531
  return QueryResult(
535
532
  entries=entries,
536
533
  total_count=len(entries),
537
534
  query_time=time.time() - start_time,
538
- index_used='time_index'
535
+ index_used="time_index",
539
536
  )
540
-
541
- def get_agent_memories(
542
- self,
543
- agent_id: str,
544
- limit: int = 50
545
- ) -> QueryResult:
537
+
538
+ def get_agent_memories(self, agent_id: str, limit: int = 50) -> QueryResult:
546
539
  """Get all memories for a specific agent.
547
-
540
+
548
541
  Args:
549
542
  agent_id: Agent ID
550
543
  limit: Maximum results
551
-
544
+
552
545
  Returns:
553
546
  Agent's memories
554
547
  """
555
548
  start_time = time.time()
556
-
549
+
557
550
  # Use agent index
558
551
  entry_ids = list(self.agent_index.get(agent_id, set()))[:limit]
559
-
552
+
560
553
  # Get entries
561
554
  entries = [self.memories[id] for id in entry_ids if id in self.memories]
562
-
555
+
563
556
  # Sort by timestamp
564
557
  entries.sort(key=lambda e: -e.timestamp.timestamp())
565
-
558
+
566
559
  return QueryResult(
567
560
  entries=entries,
568
561
  total_count=len(self.agent_index.get(agent_id, set())),
569
562
  query_time=time.time() - start_time,
570
- index_used='agent_index'
563
+ index_used="agent_index",
571
564
  )
572
-
565
+
573
566
  def _generate_id(self, agent_id: str, content: str) -> str:
574
567
  """Generate unique ID for memory entry."""
575
568
  timestamp = datetime.now().isoformat()
576
569
  hash_input = f"{agent_id}:{content[:100]}:{timestamp}"
577
570
  return hashlib.md5(hash_input.encode()).hexdigest()[:12]
578
-
571
+
579
572
  def _save_indexes(self):
580
573
  """Persist all indexes to disk."""
581
574
  # Save text index
582
575
  self.text_index.save(self.data_dir / "text_index.pkl")
583
-
576
+
584
577
  # Save other indexes
585
- with open(self.data_dir / "indexes.pkl", 'wb') as f:
586
- pickle.dump({
587
- 'memories': self.memories,
588
- 'time_index': self.time_index.index,
589
- 'agent_index': dict(self.agent_index),
590
- 'category_index': dict(self.category_index),
591
- 'tag_index': dict(self.tag_index)
592
- }, f)
593
-
578
+ with open(self.data_dir / "indexes.pkl", "wb") as f:
579
+ pickle.dump(
580
+ {
581
+ "memories": self.memories,
582
+ "time_index": self.time_index.index,
583
+ "agent_index": dict(self.agent_index),
584
+ "category_index": dict(self.category_index),
585
+ "tag_index": dict(self.tag_index),
586
+ },
587
+ f,
588
+ )
589
+
594
590
  self.logger.info(f"Saved {len(self.memories)} memories to disk")
595
-
591
+
596
592
  def _load_indexes(self):
597
593
  """Load indexes from disk."""
598
594
  # Load text index
599
595
  text_index_path = self.data_dir / "text_index.pkl"
600
596
  if text_index_path.exists():
601
597
  self.text_index.load(text_index_path)
602
-
598
+
603
599
  # Load other indexes
604
600
  indexes_path = self.data_dir / "indexes.pkl"
605
601
  if indexes_path.exists():
606
- with open(indexes_path, 'rb') as f:
602
+ with open(indexes_path, "rb") as f:
607
603
  data = pickle.load(f)
608
-
609
- self.memories = data.get('memories', {})
610
- self.time_index.index = data.get('time_index', [])
611
- self.agent_index = defaultdict(set, {k: set(v) for k, v in data.get('agent_index', {}).items()})
612
- self.category_index = defaultdict(set, {k: set(v) for k, v in data.get('category_index', {}).items()})
613
- self.tag_index = defaultdict(set, {k: set(v) for k, v in data.get('tag_index', {}).items()})
614
-
604
+
605
+ self.memories = data.get("memories", {})
606
+ self.time_index.index = data.get("time_index", [])
607
+ self.agent_index = defaultdict(
608
+ set, {k: set(v) for k, v in data.get("agent_index", {}).items()}
609
+ )
610
+ self.category_index = defaultdict(
611
+ set, {k: set(v) for k, v in data.get("category_index", {}).items()}
612
+ )
613
+ self.tag_index = defaultdict(
614
+ set, {k: set(v) for k, v in data.get("tag_index", {}).items()}
615
+ )
616
+
615
617
  self.logger.info(f"Loaded {len(self.memories)} memories from disk")
616
-
618
+
617
619
  def get_stats(self) -> Dict[str, Any]:
618
620
  """Get memory service statistics."""
619
621
  return {
620
- 'total_memories': len(self.memories),
621
- 'agents': len(self.agent_index),
622
- 'categories': len(self.category_index),
623
- 'tags': len(self.tag_index),
624
- 'index_size': {
625
- 'text': len(self.text_index.index),
626
- 'time': len(self.time_index.index),
627
- 'agent': sum(len(v) for v in self.agent_index.values()),
628
- 'category': sum(len(v) for v in self.category_index.values()),
629
- 'tag': sum(len(v) for v in self.tag_index.values())
622
+ "total_memories": len(self.memories),
623
+ "agents": len(self.agent_index),
624
+ "categories": len(self.category_index),
625
+ "tags": len(self.tag_index),
626
+ "index_size": {
627
+ "text": len(self.text_index.index),
628
+ "time": len(self.time_index.index),
629
+ "agent": sum(len(v) for v in self.agent_index.values()),
630
+ "category": sum(len(v) for v in self.category_index.values()),
631
+ "tag": sum(len(v) for v in self.tag_index.values()),
630
632
  },
631
- 'cache_stats': self.cache.get_stats()
633
+ "cache_stats": self.cache.get_stats(),
632
634
  }
633
-
635
+
634
636
  def cleanup(self):
635
637
  """Save indexes and cleanup resources."""
636
638
  self._save_indexes()
@@ -645,4 +647,4 @@ def get_indexed_memory() -> IndexedMemoryService:
645
647
  global _memory_service
646
648
  if _memory_service is None:
647
649
  _memory_service = IndexedMemoryService()
648
- return _memory_service
650
+ return _memory_service