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.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +1 -1
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +79 -51
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +20 -20
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +140 -905
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1156
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +71 -73
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +35 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +233 -199
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +30 -13
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +13 -20
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +87 -84
- claude_mpm/services/mcp_gateway/main.py +287 -137
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +97 -94
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/server/mcp_gateway.py +105 -110
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +109 -119
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +19 -533
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +2 -2
- claude_mpm/storage/state_storage.py +177 -181
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/METADATA +27 -1
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/cli/commands/run_guarded.py +0 -511
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/config/memory_guardian_yaml.py +0 -335
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/core/memory_aware_runner.py +0 -353
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/graceful_degradation.py +0 -616
- claude_mpm/services/infrastructure/health_monitor.py +0 -775
- claude_mpm/services/infrastructure/memory_dashboard.py +0 -479
- claude_mpm/services/infrastructure/memory_guardian.py +0 -944
- claude_mpm/services/infrastructure/restart_protection.py +0 -642
- claude_mpm/services/infrastructure/state_manager.py +0 -774
- claude_mpm/services/mcp_gateway/manager.py +0 -334
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.11.dist-info/RECORD +0 -306
- {claude_mpm-3.9.11.dist-info → claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {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 | 
             
            """
         | 
| 2 4 | 
             
            Async Claude Session Response Logger with Optimized Performance
         | 
| 3 5 |  | 
| @@ -15,23 +17,19 @@ Key Features: | |
| 15 17 |  | 
| 16 18 | 
             
            import asyncio
         | 
| 17 19 | 
             
            import json
         | 
| 20 | 
            +
            import logging
         | 
| 21 | 
            +
            import logging.handlers
         | 
| 18 22 | 
             
            import os
         | 
| 19 23 | 
             
            import sys
         | 
| 20 24 | 
             
            import time
         | 
| 25 | 
            +
            from dataclasses import asdict, dataclass
         | 
| 21 26 | 
             
            from datetime import datetime
         | 
| 22 | 
            -
            from pathlib import Path
         | 
| 23 | 
            -
            from typing import Dict, Any, Optional, Callable
         | 
| 24 | 
            -
            from queue import Queue, Full
         | 
| 25 | 
            -
            from threading import Thread, Lock
         | 
| 26 | 
            -
            import logging
         | 
| 27 | 
            -
            import logging.handlers
         | 
| 28 | 
            -
            from dataclasses import dataclass, asdict
         | 
| 29 27 | 
             
            from enum import Enum
         | 
| 30 | 
            -
            from  | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 28 | 
            +
            from queue import Full, Queue
         | 
| 29 | 
            +
            from threading import Lock, Thread
         | 
| 30 | 
            +
            from typing import Any, Callable, Dict, Optional
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            from claude_mpm.core.constants import PerformanceConfig, SystemLimits, TimeoutConfig
         | 
| 35 33 |  | 
| 36 34 | 
             
            # Import configuration manager
         | 
| 37 35 | 
             
            from ..core.config import Config
         | 
| @@ -41,6 +39,7 @@ logger = logging.getLogger(__name__) | |
| 41 39 |  | 
| 42 40 | 
             
            class LogFormat(Enum):
         | 
| 43 41 | 
             
                """Supported log formats for response storage."""
         | 
| 42 | 
            +
             | 
| 44 43 | 
             
                JSON = "json"
         | 
| 45 44 | 
             
                SYSLOG = "syslog"
         | 
| 46 45 | 
             
                JOURNALD = "journald"
         | 
| @@ -49,6 +48,7 @@ class LogFormat(Enum): | |
| 49 48 | 
             
            @dataclass
         | 
| 50 49 | 
             
            class LogEntry:
         | 
| 51 50 | 
             
                """Represents a log entry to be written."""
         | 
| 51 | 
            +
             | 
| 52 52 | 
             
                timestamp: str
         | 
| 53 53 | 
             
                agent: str  # Standardized field name
         | 
| 54 54 | 
             
                session_id: str
         | 
| @@ -61,7 +61,7 @@ class LogEntry: | |
| 61 61 | 
             
            class AsyncSessionLogger:
         | 
| 62 62 | 
             
                """
         | 
| 63 63 | 
             
                High-performance async logger with timestamp-based filenames.
         | 
| 64 | 
            -
             | 
| 64 | 
            +
             | 
| 65 65 | 
             
                Features:
         | 
| 66 66 | 
             
                - Non-blocking async writes with background queue processing
         | 
| 67 67 | 
             
                - Timestamp-based filenames to eliminate lookup overhead
         | 
| @@ -69,7 +69,7 @@ class AsyncSessionLogger: | |
| 69 69 | 
             
                - Fire-and-forget pattern for zero latency impact
         | 
| 70 70 | 
             
                - Graceful degradation on errors
         | 
| 71 71 | 
             
                """
         | 
| 72 | 
            -
             | 
| 72 | 
            +
             | 
| 73 73 | 
             
                def __init__(
         | 
| 74 74 | 
             
                    self,
         | 
| 75 75 | 
             
                    base_dir: Optional[Path] = None,
         | 
| @@ -77,11 +77,11 @@ class AsyncSessionLogger: | |
| 77 77 | 
             
                    max_queue_size: Optional[int] = None,
         | 
| 78 78 | 
             
                    enable_async: Optional[bool] = None,
         | 
| 79 79 | 
             
                    enable_compression: Optional[bool] = None,
         | 
| 80 | 
            -
                    config: Optional[Config] = None
         | 
| 80 | 
            +
                    config: Optional[Config] = None,
         | 
| 81 81 | 
             
                ):
         | 
| 82 82 | 
             
                    """
         | 
| 83 83 | 
             
                    Initialize the async session logger.
         | 
| 84 | 
            -
             | 
| 84 | 
            +
             | 
| 85 85 | 
             
                    Args:
         | 
| 86 86 | 
             
                        base_dir: Base directory for responses (overrides config)
         | 
| 87 87 | 
             
                        log_format: Format to use for logging (overrides config)
         | 
| @@ -94,86 +94,101 @@ class AsyncSessionLogger: | |
| 94 94 | 
             
                    if config is None:
         | 
| 95 95 | 
             
                        config = Config()
         | 
| 96 96 | 
             
                    self.config = config
         | 
| 97 | 
            -
             | 
| 97 | 
            +
             | 
| 98 98 | 
             
                    # Get response logging configuration section
         | 
| 99 | 
            -
                    response_config = self.config.get( | 
| 100 | 
            -
             | 
| 99 | 
            +
                    response_config = self.config.get("response_logging", {})
         | 
| 100 | 
            +
             | 
| 101 101 | 
             
                    # Apply configuration with parameter overrides
         | 
| 102 | 
            -
                    self.base_dir = Path( | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 102 | 
            +
                    self.base_dir = Path(
         | 
| 103 | 
            +
                        base_dir
         | 
| 104 | 
            +
                        or response_config.get("session_directory", ".claude-mpm/responses")
         | 
| 105 | 
            +
                    )
         | 
| 106 | 
            +
             | 
| 105 107 | 
             
                    # Convert log format string to enum
         | 
| 106 | 
            -
                    format_str = response_config.get( | 
| 108 | 
            +
                    format_str = response_config.get("format", "json").lower()
         | 
| 107 109 | 
             
                    if log_format is not None:
         | 
| 108 110 | 
             
                        self.log_format = log_format
         | 
| 109 | 
            -
                    elif format_str ==  | 
| 111 | 
            +
                    elif format_str == "syslog":
         | 
| 110 112 | 
             
                        self.log_format = LogFormat.SYSLOG
         | 
| 111 | 
            -
                    elif format_str ==  | 
| 113 | 
            +
                    elif format_str == "journald":
         | 
| 112 114 | 
             
                        self.log_format = LogFormat.JOURNALD
         | 
| 113 115 | 
             
                    else:
         | 
| 114 116 | 
             
                        self.log_format = LogFormat.JSON
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                    self.max_queue_size =  | 
| 117 | 
            -
             | 
| 117 | 
            +
             | 
| 118 | 
            +
                    self.max_queue_size = (
         | 
| 119 | 
            +
                        max_queue_size
         | 
| 120 | 
            +
                        if max_queue_size is not None
         | 
| 121 | 
            +
                        else response_config.get("max_queue_size", SystemLimits.MAX_QUEUE_SIZE)
         | 
| 122 | 
            +
                    )
         | 
| 123 | 
            +
             | 
| 118 124 | 
             
                    # Handle async configuration with backward compatibility
         | 
| 119 125 | 
             
                    if enable_async is not None:
         | 
| 120 126 | 
             
                        self.enable_async = enable_async
         | 
| 121 127 | 
             
                    else:
         | 
| 122 128 | 
             
                        # Check configuration first, then environment variables for backward compatibility
         | 
| 123 | 
            -
                        self.enable_async = response_config.get( | 
| 129 | 
            +
                        self.enable_async = response_config.get("use_async", True)
         | 
| 124 130 | 
             
                        # Override with environment variable if set (backward compatibility)
         | 
| 125 | 
            -
                        if os.environ.get( | 
| 126 | 
            -
                            self.enable_async =  | 
| 127 | 
            -
             | 
| 131 | 
            +
                        if os.environ.get("CLAUDE_USE_ASYNC_LOG"):
         | 
| 132 | 
            +
                            self.enable_async = (
         | 
| 133 | 
            +
                                os.environ.get("CLAUDE_USE_ASYNC_LOG", "true").lower() == "true"
         | 
| 134 | 
            +
                            )
         | 
| 135 | 
            +
             | 
| 128 136 | 
             
                    # Check debug sync mode (forces synchronous for debugging)
         | 
| 129 | 
            -
                    if  | 
| 137 | 
            +
                    if (
         | 
| 138 | 
            +
                        response_config.get("debug_sync", False)
         | 
| 139 | 
            +
                        or os.environ.get("CLAUDE_LOG_SYNC", "").lower() == "true"
         | 
| 140 | 
            +
                    ):
         | 
| 130 141 | 
             
                        logger.info("Debug sync mode enabled - forcing synchronous logging")
         | 
| 131 142 | 
             
                        self.enable_async = False
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                    self.enable_compression =  | 
| 134 | 
            -
             | 
| 143 | 
            +
             | 
| 144 | 
            +
                    self.enable_compression = (
         | 
| 145 | 
            +
                        enable_compression
         | 
| 146 | 
            +
                        if enable_compression is not None
         | 
| 147 | 
            +
                        else response_config.get("enable_compression", False)
         | 
| 148 | 
            +
                    )
         | 
| 149 | 
            +
             | 
| 135 150 | 
             
                    # Create base directory
         | 
| 136 151 | 
             
                    self.base_dir.mkdir(parents=True, exist_ok=True)
         | 
| 137 | 
            -
             | 
| 152 | 
            +
             | 
| 138 153 | 
             
                    # Session management
         | 
| 139 154 | 
             
                    self.session_id = self._get_claude_session_id()
         | 
| 140 | 
            -
             | 
| 155 | 
            +
             | 
| 141 156 | 
             
                    # Async infrastructure
         | 
| 142 157 | 
             
                    self._queue: Queue = Queue(maxsize=self.max_queue_size)
         | 
| 143 158 | 
             
                    self._worker_thread: Optional[Thread] = None
         | 
| 144 159 | 
             
                    self._shutdown = False
         | 
| 145 160 | 
             
                    self._lock = Lock()
         | 
| 146 | 
            -
             | 
| 161 | 
            +
             | 
| 147 162 | 
             
                    # Statistics
         | 
| 148 163 | 
             
                    self.stats = {
         | 
| 149 164 | 
             
                        "logged": 0,
         | 
| 150 165 | 
             
                        "queued": 0,
         | 
| 151 166 | 
             
                        "dropped": 0,
         | 
| 152 167 | 
             
                        "errors": 0,
         | 
| 153 | 
            -
                        "avg_write_time_ms": 0.0
         | 
| 168 | 
            +
                        "avg_write_time_ms": 0.0,
         | 
| 154 169 | 
             
                    }
         | 
| 155 | 
            -
             | 
| 170 | 
            +
             | 
| 156 171 | 
             
                    # Initialize format-specific handlers
         | 
| 157 172 | 
             
                    self._init_format_handler()
         | 
| 158 | 
            -
             | 
| 173 | 
            +
             | 
| 159 174 | 
             
                    # Start background worker if async enabled
         | 
| 160 175 | 
             
                    if self.enable_async:
         | 
| 161 176 | 
             
                        self._start_worker()
         | 
| 162 | 
            -
             | 
| 177 | 
            +
             | 
| 163 178 | 
             
                def _get_claude_session_id(self) -> str:
         | 
| 164 179 | 
             
                    """Get or generate a Claude session ID."""
         | 
| 165 180 | 
             
                    # Check environment variables in order of preference
         | 
| 166 | 
            -
                    for env_var in [ | 
| 181 | 
            +
                    for env_var in ["CLAUDE_SESSION_ID", "ANTHROPIC_SESSION_ID", "SESSION_ID"]:
         | 
| 167 182 | 
             
                        session_id = os.environ.get(env_var)
         | 
| 168 183 | 
             
                        if session_id:
         | 
| 169 184 | 
             
                            logger.info(f"Using session ID from {env_var}: {session_id}")
         | 
| 170 185 | 
             
                            return session_id
         | 
| 171 | 
            -
             | 
| 186 | 
            +
             | 
| 172 187 | 
             
                    # Generate timestamp-based session ID
         | 
| 173 | 
            -
                    session_id = datetime.now().strftime( | 
| 188 | 
            +
                    session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
         | 
| 174 189 | 
             
                    logger.info(f"Generated session ID: {session_id}")
         | 
| 175 190 | 
             
                    return session_id
         | 
| 176 | 
            -
             | 
| 191 | 
            +
             | 
| 177 192 | 
             
                def _init_format_handler(self):
         | 
| 178 193 | 
             
                    """Initialize format-specific logging handlers."""
         | 
| 179 194 | 
             
                    if self.log_format == LogFormat.SYSLOG:
         | 
| @@ -185,65 +200,66 @@ class AsyncSessionLogger: | |
| 185 200 | 
             
                                address = "/dev/log"
         | 
| 186 201 | 
             
                            else:
         | 
| 187 202 | 
             
                                address = ("localhost", 514)
         | 
| 188 | 
            -
             | 
| 203 | 
            +
             | 
| 189 204 | 
             
                            self.syslog_handler = logging.handlers.SysLogHandler(address=address)
         | 
| 190 205 | 
             
                            self.syslog_handler.setFormatter(
         | 
| 191 | 
            -
                                logging.Formatter( | 
| 206 | 
            +
                                logging.Formatter("claude-mpm[%(process)d]: %(message)s")
         | 
| 192 207 | 
             
                            )
         | 
| 193 208 | 
             
                            logger.info("Initialized syslog handler")
         | 
| 194 209 | 
             
                        except Exception as e:
         | 
| 195 210 | 
             
                            logger.warning(f"Failed to init syslog, falling back to JSON: {e}")
         | 
| 196 211 | 
             
                            self.log_format = LogFormat.JSON
         | 
| 197 | 
            -
             | 
| 212 | 
            +
             | 
| 198 213 | 
             
                    elif self.log_format == LogFormat.JOURNALD:
         | 
| 199 214 | 
             
                        # Use systemd journal for Linux systems
         | 
| 200 215 | 
             
                        try:
         | 
| 201 216 | 
             
                            from systemd.journal import JournalHandler
         | 
| 217 | 
            +
             | 
| 202 218 | 
             
                            self.journal_handler = JournalHandler()
         | 
| 203 | 
            -
                            self.journal_handler.setFormatter(
         | 
| 204 | 
            -
                                logging.Formatter('%(message)s')
         | 
| 205 | 
            -
                            )
         | 
| 219 | 
            +
                            self.journal_handler.setFormatter(logging.Formatter("%(message)s"))
         | 
| 206 220 | 
             
                            logger.info("Initialized journald handler")
         | 
| 207 221 | 
             
                        except ImportError:
         | 
| 208 222 | 
             
                            logger.warning("systemd not available, falling back to JSON")
         | 
| 209 223 | 
             
                            self.log_format = LogFormat.JSON
         | 
| 210 | 
            -
             | 
| 224 | 
            +
             | 
| 211 225 | 
             
                def _start_worker(self):
         | 
| 212 226 | 
             
                    """Start the background worker thread for async writes."""
         | 
| 213 227 | 
             
                    with self._lock:
         | 
| 214 228 | 
             
                        if self._worker_thread is None or not self._worker_thread.is_alive():
         | 
| 215 229 | 
             
                            self._shutdown = False
         | 
| 216 230 | 
             
                            self._worker_thread = Thread(
         | 
| 217 | 
            -
                                target=self._process_queue,
         | 
| 218 | 
            -
                                name="AsyncLoggerWorker",
         | 
| 219 | 
            -
                                daemon=True
         | 
| 231 | 
            +
                                target=self._process_queue, name="AsyncLoggerWorker", daemon=True
         | 
| 220 232 | 
             
                            )
         | 
| 221 233 | 
             
                            self._worker_thread.start()
         | 
| 222 234 | 
             
                            logger.debug("Started async logger worker thread")
         | 
| 223 | 
            -
             | 
| 235 | 
            +
             | 
| 224 236 | 
             
                def _process_queue(self):
         | 
| 225 237 | 
             
                    """Background worker to process the log queue."""
         | 
| 226 238 | 
             
                    write_times = []
         | 
| 227 | 
            -
             | 
| 239 | 
            +
             | 
| 228 240 | 
             
                    while not self._shutdown:
         | 
| 229 241 | 
             
                        try:
         | 
| 230 242 | 
             
                            # Get entry with timeout to allow shutdown checks
         | 
| 231 243 | 
             
                            entry = self._queue.get(timeout=TimeoutConfig.QUEUE_GET_TIMEOUT)
         | 
| 232 | 
            -
             | 
| 244 | 
            +
             | 
| 233 245 | 
             
                            # Time the write operation
         | 
| 234 246 | 
             
                            start_time = time.perf_counter()
         | 
| 235 247 | 
             
                            self._write_entry(entry)
         | 
| 236 | 
            -
                            write_time = ( | 
| 237 | 
            -
             | 
| 248 | 
            +
                            write_time = (
         | 
| 249 | 
            +
                                time.perf_counter() - start_time
         | 
| 250 | 
            +
                            ) * PerformanceConfig.SECONDS_TO_MS
         | 
| 251 | 
            +
             | 
| 238 252 | 
             
                            # Update statistics
         | 
| 239 253 | 
             
                            write_times.append(write_time)
         | 
| 240 254 | 
             
                            if len(write_times) > 100:
         | 
| 241 255 | 
             
                                write_times = write_times[-100:]  # Keep last 100
         | 
| 242 | 
            -
             | 
| 256 | 
            +
             | 
| 243 257 | 
             
                            with self._lock:
         | 
| 244 258 | 
             
                                self.stats["logged"] += 1
         | 
| 245 | 
            -
                                self.stats["avg_write_time_ms"] = sum(write_times) / len( | 
| 246 | 
            -
             | 
| 259 | 
            +
                                self.stats["avg_write_time_ms"] = sum(write_times) / len(
         | 
| 260 | 
            +
                                    write_times
         | 
| 261 | 
            +
                                )
         | 
| 262 | 
            +
             | 
| 247 263 | 
             
                        except Exception as e:
         | 
| 248 264 | 
             
                            # Check if it's a timeout (queue.Empty) or real error
         | 
| 249 265 | 
             
                            if "Empty" not in str(type(e).__name__):
         | 
| @@ -251,7 +267,7 @@ class AsyncSessionLogger: | |
| 251 267 | 
             
                                with self._lock:
         | 
| 252 268 | 
             
                                    self.stats["errors"] += 1
         | 
| 253 269 | 
             
                            # Otherwise it's just a timeout, continue to check shutdown
         | 
| 254 | 
            -
             | 
| 270 | 
            +
             | 
| 255 271 | 
             
                def _write_entry(self, entry: LogEntry):
         | 
| 256 272 | 
             
                    """Write a log entry to disk or system log."""
         | 
| 257 273 | 
             
                    try:
         | 
| @@ -265,63 +281,66 @@ class AsyncSessionLogger: | |
| 265 281 | 
             
                        logger.error(f"Failed to write log entry: {e}", exc_info=True)
         | 
| 266 282 | 
             
                        with self._lock:
         | 
| 267 283 | 
             
                            self.stats["errors"] += 1
         | 
| 268 | 
            -
             | 
| 284 | 
            +
             | 
| 269 285 | 
             
                def _generate_filename(self, entry: LogEntry) -> str:
         | 
| 270 286 | 
             
                    """
         | 
| 271 287 | 
             
                    Generate a flat filename with session ID, agent, and timestamp.
         | 
| 272 | 
            -
             | 
| 288 | 
            +
             | 
| 273 289 | 
             
                    Args:
         | 
| 274 290 | 
             
                        entry: Log entry with session, agent, and timestamp info
         | 
| 275 | 
            -
             | 
| 291 | 
            +
             | 
| 276 292 | 
             
                    Returns:
         | 
| 277 293 | 
             
                        Filename in format: [session_id]-[agent]-timestamp.json
         | 
| 278 294 | 
             
                    """
         | 
| 279 295 | 
             
                    # Format timestamp for filename (remove special chars)
         | 
| 280 | 
            -
                    timestamp_str =  | 
| 281 | 
            -
             | 
| 296 | 
            +
                    timestamp_str = (
         | 
| 297 | 
            +
                        entry.timestamp.replace(":", "").replace("-", "").replace(".", "_")
         | 
| 298 | 
            +
                    )
         | 
| 299 | 
            +
             | 
| 282 300 | 
             
                    # Create filename: session_id-agent-timestamp.json
         | 
| 283 301 | 
             
                    filename = f"{entry.session_id}-{entry.agent}-{timestamp_str}.json"
         | 
| 284 302 | 
             
                    if self.enable_compression:
         | 
| 285 303 | 
             
                        filename += ".gz"
         | 
| 286 304 | 
             
                    return filename
         | 
| 287 | 
            -
             | 
| 305 | 
            +
             | 
| 288 306 | 
             
                def _write_json_entry(self, entry: LogEntry):
         | 
| 289 307 | 
             
                    """Write entry as JSON file with timestamp-based filename."""
         | 
| 290 308 | 
             
                    # Ensure base directory exists (flat structure, no subdirs)
         | 
| 291 309 | 
             
                    self.base_dir.mkdir(parents=True, exist_ok=True)
         | 
| 292 | 
            -
             | 
| 310 | 
            +
             | 
| 293 311 | 
             
                    # Generate flat filename
         | 
| 294 312 | 
             
                    filename = self._generate_filename(entry)
         | 
| 295 313 | 
             
                    file_path = self.base_dir / filename
         | 
| 296 | 
            -
             | 
| 314 | 
            +
             | 
| 297 315 | 
             
                    # Prepare data (exclude microseconds field which is internal only)
         | 
| 298 316 | 
             
                    data = asdict(entry)
         | 
| 299 317 | 
             
                    # Remove internal-only field
         | 
| 300 | 
            -
                    data.pop( | 
| 301 | 
            -
             | 
| 318 | 
            +
                    data.pop("microseconds", None)
         | 
| 319 | 
            +
             | 
| 302 320 | 
             
                    # Write file
         | 
| 303 321 | 
             
                    if self.enable_compression:
         | 
| 304 322 | 
             
                        import gzip
         | 
| 305 | 
            -
             | 
| 323 | 
            +
             | 
| 324 | 
            +
                        with gzip.open(file_path, "wt", encoding="utf-8") as f:
         | 
| 306 325 | 
             
                            json.dump(data, f, indent=2, ensure_ascii=False)
         | 
| 307 326 | 
             
                    else:
         | 
| 308 | 
            -
                        with open(file_path,  | 
| 327 | 
            +
                        with open(file_path, "w", encoding="utf-8") as f:
         | 
| 309 328 | 
             
                            json.dump(data, f, indent=2, ensure_ascii=False)
         | 
| 310 | 
            -
             | 
| 329 | 
            +
             | 
| 311 330 | 
             
                    logger.debug(f"Wrote log entry to {file_path}")
         | 
| 312 | 
            -
             | 
| 331 | 
            +
             | 
| 313 332 | 
             
                def _write_syslog_entry(self, entry: LogEntry):
         | 
| 314 333 | 
             
                    """Write entry to syslog for OS-level performance."""
         | 
| 315 | 
            -
                    if hasattr(self,  | 
| 334 | 
            +
                    if hasattr(self, "syslog_handler"):
         | 
| 316 335 | 
             
                        # Format as structured log message with standardized field names
         | 
| 317 336 | 
             
                        msg = (
         | 
| 318 337 | 
             
                            f"agent={entry.agent} "
         | 
| 319 338 | 
             
                            f"session={entry.session_id} "
         | 
| 320 | 
            -
                            f | 
| 339 | 
            +
                            f'request="{entry.request[:100]}" '
         | 
| 321 340 | 
             
                            f"response_len={len(entry.response)} "
         | 
| 322 341 | 
             
                            f"metadata={json.dumps(entry.metadata)}"
         | 
| 323 342 | 
             
                        )
         | 
| 324 | 
            -
             | 
| 343 | 
            +
             | 
| 325 344 | 
             
                        record = logging.LogRecord(
         | 
| 326 345 | 
             
                            name="claude-mpm",
         | 
| 327 346 | 
             
                            level=logging.INFO,
         | 
| @@ -329,14 +348,14 @@ class AsyncSessionLogger: | |
| 329 348 | 
             
                            lineno=0,
         | 
| 330 349 | 
             
                            msg=msg,
         | 
| 331 350 | 
             
                            args=(),
         | 
| 332 | 
            -
                            exc_info=None
         | 
| 351 | 
            +
                            exc_info=None,
         | 
| 333 352 | 
             
                        )
         | 
| 334 | 
            -
             | 
| 353 | 
            +
             | 
| 335 354 | 
             
                        self.syslog_handler.emit(record)
         | 
| 336 | 
            -
             | 
| 355 | 
            +
             | 
| 337 356 | 
             
                def _write_journald_entry(self, entry: LogEntry):
         | 
| 338 357 | 
             
                    """Write entry to systemd journal."""
         | 
| 339 | 
            -
                    if hasattr(self,  | 
| 358 | 
            +
                    if hasattr(self, "journal_handler"):
         | 
| 340 359 | 
             
                        # Create structured journal entry with standardized field names
         | 
| 341 360 | 
             
                        record = logging.LogRecord(
         | 
| 342 361 | 
             
                            name="claude-mpm",
         | 
| @@ -345,36 +364,38 @@ class AsyncSessionLogger: | |
| 345 364 | 
             
                            lineno=0,
         | 
| 346 365 | 
             
                            msg=f"Claude MPM Response: {entry.request[:100]}",
         | 
| 347 366 | 
             
                            args=(),
         | 
| 348 | 
            -
                            exc_info=None
         | 
| 367 | 
            +
                            exc_info=None,
         | 
| 349 368 | 
             
                        )
         | 
| 350 | 
            -
             | 
| 369 | 
            +
             | 
| 351 370 | 
             
                        # Add structured fields with standardized names
         | 
| 352 | 
            -
                        record.__dict__.update( | 
| 353 | 
            -
                             | 
| 354 | 
            -
             | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 357 | 
            -
             | 
| 358 | 
            -
             | 
| 359 | 
            -
             | 
| 371 | 
            +
                        record.__dict__.update(
         | 
| 372 | 
            +
                            {
         | 
| 373 | 
            +
                                "AGENT": entry.agent,
         | 
| 374 | 
            +
                                "SESSION_ID": entry.session_id,
         | 
| 375 | 
            +
                                "REQUEST": entry.request,
         | 
| 376 | 
            +
                                "RESPONSE_LENGTH": len(entry.response),
         | 
| 377 | 
            +
                                "METADATA": json.dumps(entry.metadata),
         | 
| 378 | 
            +
                            }
         | 
| 379 | 
            +
                        )
         | 
| 380 | 
            +
             | 
| 360 381 | 
             
                        self.journal_handler.emit(record)
         | 
| 361 | 
            -
             | 
| 382 | 
            +
             | 
| 362 383 | 
             
                def log_response(
         | 
| 363 384 | 
             
                    self,
         | 
| 364 385 | 
             
                    request_summary: str,
         | 
| 365 386 | 
             
                    response_content: str,
         | 
| 366 387 | 
             
                    metadata: Optional[Dict[str, Any]] = None,
         | 
| 367 | 
            -
                    agent: Optional[str] = None
         | 
| 388 | 
            +
                    agent: Optional[str] = None,
         | 
| 368 389 | 
             
                ) -> bool:
         | 
| 369 390 | 
             
                    """
         | 
| 370 391 | 
             
                    Log a response with fire-and-forget async pattern.
         | 
| 371 | 
            -
             | 
| 392 | 
            +
             | 
| 372 393 | 
             
                    Args:
         | 
| 373 394 | 
             
                        request_summary: Brief summary of the request
         | 
| 374 395 | 
             
                        response_content: The full response content
         | 
| 375 396 | 
             
                        metadata: Optional metadata (agent name, model, etc.)
         | 
| 376 397 | 
             
                        agent: Optional agent name (overrides metadata)
         | 
| 377 | 
            -
             | 
| 398 | 
            +
             | 
| 378 399 | 
             
                    Returns:
         | 
| 379 400 | 
             
                        True if queued successfully, False if dropped
         | 
| 380 401 | 
             
                    """
         | 
| @@ -384,12 +405,12 @@ class AsyncSessionLogger: | |
| 384 405 | 
             
                        agent_name = agent.replace(" ", "_").lower()
         | 
| 385 406 | 
             
                    elif metadata and "agent" in metadata:
         | 
| 386 407 | 
             
                        agent_name = metadata["agent"].replace(" ", "_").lower()
         | 
| 387 | 
            -
             | 
| 408 | 
            +
             | 
| 388 409 | 
             
                    # Create timestamp with microsecond precision
         | 
| 389 410 | 
             
                    now = datetime.now()
         | 
| 390 411 | 
             
                    timestamp = now.isoformat()
         | 
| 391 412 | 
             
                    microseconds = now.microsecond
         | 
| 392 | 
            -
             | 
| 413 | 
            +
             | 
| 393 414 | 
             
                    # Create log entry with standardized field names
         | 
| 394 415 | 
             
                    entry = LogEntry(
         | 
| 395 416 | 
             
                        timestamp=timestamp,
         | 
| @@ -398,9 +419,9 @@ class AsyncSessionLogger: | |
| 398 419 | 
             
                        request=request_summary,  # Standardized field name
         | 
| 399 420 | 
             
                        response=response_content,  # Standardized field name
         | 
| 400 421 | 
             
                        metadata=metadata or {},
         | 
| 401 | 
            -
                        microseconds=microseconds
         | 
| 422 | 
            +
                        microseconds=microseconds,
         | 
| 402 423 | 
             
                    )
         | 
| 403 | 
            -
             | 
| 424 | 
            +
             | 
| 404 425 | 
             
                    # Queue for async processing or write directly
         | 
| 405 426 | 
             
                    if self.enable_async:
         | 
| 406 427 | 
             
                        try:
         | 
| @@ -418,62 +439,64 @@ class AsyncSessionLogger: | |
| 418 439 | 
             
                        # Synchronous write for debugging
         | 
| 419 440 | 
             
                        self._write_entry(entry)
         | 
| 420 441 | 
             
                        return True
         | 
| 421 | 
            -
             | 
| 442 | 
            +
             | 
| 422 443 | 
             
                def flush(self, timeout: float = 5.0) -> bool:
         | 
| 423 444 | 
             
                    """
         | 
| 424 445 | 
             
                    Flush pending log entries with timeout.
         | 
| 425 | 
            -
             | 
| 446 | 
            +
             | 
| 426 447 | 
             
                    Args:
         | 
| 427 448 | 
             
                        timeout: Maximum time to wait for flush
         | 
| 428 | 
            -
             | 
| 449 | 
            +
             | 
| 429 450 | 
             
                    Returns:
         | 
| 430 451 | 
             
                        True if all entries flushed, False if timeout
         | 
| 431 452 | 
             
                    """
         | 
| 432 453 | 
             
                    if not self.enable_async:
         | 
| 433 454 | 
             
                        return True
         | 
| 434 | 
            -
             | 
| 455 | 
            +
             | 
| 435 456 | 
             
                    start_time = time.time()
         | 
| 436 457 | 
             
                    while not self._queue.empty():
         | 
| 437 458 | 
             
                        if time.time() - start_time > timeout:
         | 
| 438 | 
            -
                            logger.warning( | 
| 459 | 
            +
                            logger.warning(
         | 
| 460 | 
            +
                                f"Flush timeout with {self._queue.qsize()} entries remaining"
         | 
| 461 | 
            +
                            )
         | 
| 439 462 | 
             
                            return False
         | 
| 440 463 | 
             
                        time.sleep(0.01)
         | 
| 441 | 
            -
             | 
| 464 | 
            +
             | 
| 442 465 | 
             
                    return True
         | 
| 443 | 
            -
             | 
| 466 | 
            +
             | 
| 444 467 | 
             
                def shutdown(self, timeout: float = 5.0):
         | 
| 445 468 | 
             
                    """
         | 
| 446 469 | 
             
                    Gracefully shutdown the logger.
         | 
| 447 | 
            -
             | 
| 470 | 
            +
             | 
| 448 471 | 
             
                    Args:
         | 
| 449 472 | 
             
                        timeout: Maximum time to wait for shutdown
         | 
| 450 473 | 
             
                    """
         | 
| 451 474 | 
             
                    if self.enable_async:
         | 
| 452 475 | 
             
                        logger.info("Shutting down async logger")
         | 
| 453 | 
            -
             | 
| 476 | 
            +
             | 
| 454 477 | 
             
                        # Signal shutdown
         | 
| 455 478 | 
             
                        self._shutdown = True
         | 
| 456 | 
            -
             | 
| 479 | 
            +
             | 
| 457 480 | 
             
                        # Wait for worker to finish
         | 
| 458 481 | 
             
                        if self._worker_thread and self._worker_thread.is_alive():
         | 
| 459 482 | 
             
                            self._worker_thread.join(timeout)
         | 
| 460 | 
            -
             | 
| 483 | 
            +
             | 
| 461 484 | 
             
                            if self._worker_thread.is_alive():
         | 
| 462 485 | 
             
                                logger.warning("Worker thread did not shutdown cleanly")
         | 
| 463 | 
            -
             | 
| 486 | 
            +
             | 
| 464 487 | 
             
                        # Log final statistics
         | 
| 465 488 | 
             
                        logger.info(f"Logger stats: {self.stats}")
         | 
| 466 | 
            -
             | 
| 489 | 
            +
             | 
| 467 490 | 
             
                def get_stats(self) -> Dict[str, Any]:
         | 
| 468 491 | 
             
                    """Get logger statistics."""
         | 
| 469 492 | 
             
                    with self._lock:
         | 
| 470 493 | 
             
                        return self.stats.copy()
         | 
| 471 | 
            -
             | 
| 494 | 
            +
             | 
| 472 495 | 
             
                def set_session_id(self, session_id: str):
         | 
| 473 496 | 
             
                    """Set a new session ID."""
         | 
| 474 497 | 
             
                    self.session_id = session_id
         | 
| 475 498 | 
             
                    logger.info(f"Session ID updated to: {session_id}")
         | 
| 476 | 
            -
             | 
| 499 | 
            +
             | 
| 477 500 | 
             
                def is_enabled(self) -> bool:
         | 
| 478 501 | 
             
                    """Check if logging is enabled."""
         | 
| 479 502 | 
             
                    return True  # Always enabled in this implementation
         | 
| @@ -487,71 +510,80 @@ _logger_lock = Lock() | |
| 487 510 | 
             
            def get_async_logger(
         | 
| 488 511 | 
             
                log_format: Optional[LogFormat] = None,
         | 
| 489 512 | 
             
                enable_async: Optional[bool] = None,
         | 
| 490 | 
            -
                config: Optional[Config] = None
         | 
| 513 | 
            +
                config: Optional[Config] = None,
         | 
| 491 514 | 
             
            ) -> AsyncSessionLogger:
         | 
| 492 515 | 
             
                """
         | 
| 493 516 | 
             
                Get the singleton async logger instance.
         | 
| 494 | 
            -
             | 
| 517 | 
            +
             | 
| 495 518 | 
             
                Args:
         | 
| 496 519 | 
             
                    log_format: Optional log format override
         | 
| 497 520 | 
             
                    enable_async: Enable async mode override
         | 
| 498 521 | 
             
                    config: Optional configuration instance to use
         | 
| 499 | 
            -
             | 
| 522 | 
            +
             | 
| 500 523 | 
             
                Returns:
         | 
| 501 524 | 
             
                    The shared AsyncSessionLogger instance
         | 
| 502 525 | 
             
                """
         | 
| 503 526 | 
             
                global _logger_instance
         | 
| 504 | 
            -
             | 
| 527 | 
            +
             | 
| 505 528 | 
             
                with _logger_lock:
         | 
| 506 529 | 
             
                    if _logger_instance is None:
         | 
| 507 530 | 
             
                        # Load configuration if not provided
         | 
| 508 531 | 
             
                        if config is None:
         | 
| 509 532 | 
             
                            config = Config()
         | 
| 510 | 
            -
             | 
| 533 | 
            +
             | 
| 511 534 | 
             
                        # Get response logging configuration
         | 
| 512 | 
            -
                        response_config = config.get( | 
| 513 | 
            -
             | 
| 535 | 
            +
                        response_config = config.get("response_logging", {})
         | 
| 536 | 
            +
             | 
| 514 537 | 
             
                        # Determine log format
         | 
| 515 538 | 
             
                        if log_format is None:
         | 
| 516 539 | 
             
                            # Check configuration first
         | 
| 517 | 
            -
                            format_str = response_config.get( | 
| 518 | 
            -
             | 
| 540 | 
            +
                            format_str = response_config.get("format", "json").lower()
         | 
| 541 | 
            +
             | 
| 519 542 | 
             
                            # Check environment for backward compatibility
         | 
| 520 | 
            -
                            format_env = os.environ.get( | 
| 543 | 
            +
                            format_env = os.environ.get("CLAUDE_LOG_FORMAT", "").lower()
         | 
| 521 544 | 
             
                            if format_env:
         | 
| 522 | 
            -
                                logger.info( | 
| 545 | 
            +
                                logger.info(
         | 
| 546 | 
            +
                                    f"Using CLAUDE_LOG_FORMAT environment variable (deprecated): {format_env}"
         | 
| 547 | 
            +
                                )
         | 
| 523 548 | 
             
                                format_str = format_env
         | 
| 524 | 
            -
             | 
| 525 | 
            -
                            if format_str ==  | 
| 549 | 
            +
             | 
| 550 | 
            +
                            if format_str == "syslog":
         | 
| 526 551 | 
             
                                log_format = LogFormat.SYSLOG
         | 
| 527 | 
            -
                            elif format_str ==  | 
| 552 | 
            +
                            elif format_str == "journald":
         | 
| 528 553 | 
             
                                log_format = LogFormat.JOURNALD
         | 
| 529 554 | 
             
                            else:
         | 
| 530 555 | 
             
                                log_format = LogFormat.JSON
         | 
| 531 | 
            -
             | 
| 556 | 
            +
             | 
| 532 557 | 
             
                        # Determine async mode if not specified
         | 
| 533 558 | 
             
                        if enable_async is None:
         | 
| 534 559 | 
             
                            # Configuration takes precedence
         | 
| 535 | 
            -
                            enable_async = response_config.get( | 
| 536 | 
            -
             | 
| 560 | 
            +
                            enable_async = response_config.get("use_async", True)
         | 
| 561 | 
            +
             | 
| 537 562 | 
             
                            # Check environment for backward compatibility
         | 
| 538 | 
            -
                            if os.environ.get( | 
| 539 | 
            -
                                env_async =  | 
| 540 | 
            -
             | 
| 563 | 
            +
                            if os.environ.get("CLAUDE_USE_ASYNC_LOG"):
         | 
| 564 | 
            +
                                env_async = (
         | 
| 565 | 
            +
                                    os.environ.get("CLAUDE_USE_ASYNC_LOG", "true").lower() == "true"
         | 
| 566 | 
            +
                                )
         | 
| 567 | 
            +
                                logger.info(
         | 
| 568 | 
            +
                                    f"Using CLAUDE_USE_ASYNC_LOG environment variable (deprecated): {env_async}"
         | 
| 569 | 
            +
                                )
         | 
| 541 570 | 
             
                                enable_async = env_async
         | 
| 542 | 
            -
             | 
| 571 | 
            +
             | 
| 543 572 | 
             
                            # Debug sync mode overrides everything
         | 
| 544 | 
            -
                            if  | 
| 545 | 
            -
                                 | 
| 546 | 
            -
             | 
| 573 | 
            +
                            if (
         | 
| 574 | 
            +
                                response_config.get("debug_sync", False)
         | 
| 575 | 
            +
                                or os.environ.get("CLAUDE_LOG_SYNC", "").lower() == "true"
         | 
| 576 | 
            +
                            ):
         | 
| 577 | 
            +
                                if os.environ.get("CLAUDE_LOG_SYNC"):
         | 
| 578 | 
            +
                                    logger.info(
         | 
| 579 | 
            +
                                        "Using CLAUDE_LOG_SYNC environment variable (deprecated)"
         | 
| 580 | 
            +
                                    )
         | 
| 547 581 | 
             
                                enable_async = False
         | 
| 548 | 
            -
             | 
| 582 | 
            +
             | 
| 549 583 | 
             
                        _logger_instance = AsyncSessionLogger(
         | 
| 550 | 
            -
                            log_format=log_format,
         | 
| 551 | 
            -
                            enable_async=enable_async,
         | 
| 552 | 
            -
                            config=config
         | 
| 584 | 
            +
                            log_format=log_format, enable_async=enable_async, config=config
         | 
| 553 585 | 
             
                        )
         | 
| 554 | 
            -
             | 
| 586 | 
            +
             | 
| 555 587 | 
             
                    return _logger_instance
         | 
| 556 588 |  | 
| 557 589 |  | 
| @@ -559,17 +591,17 @@ def log_response_async( | |
| 559 591 | 
             
                request_summary: str,
         | 
| 560 592 | 
             
                response_content: str,
         | 
| 561 593 | 
             
                metadata: Optional[Dict[str, Any]] = None,
         | 
| 562 | 
            -
                agent: Optional[str] = None
         | 
| 594 | 
            +
                agent: Optional[str] = None,
         | 
| 563 595 | 
             
            ) -> bool:
         | 
| 564 596 | 
             
                """
         | 
| 565 597 | 
             
                Convenience function for async response logging.
         | 
| 566 | 
            -
             | 
| 598 | 
            +
             | 
| 567 599 | 
             
                Args:
         | 
| 568 600 | 
             
                    request_summary: Brief summary of the request
         | 
| 569 601 | 
             
                    response_content: The full response content
         | 
| 570 602 | 
             
                    metadata: Optional metadata
         | 
| 571 603 | 
             
                    agent: Optional agent name
         | 
| 572 | 
            -
             | 
| 604 | 
            +
             | 
| 573 605 | 
             
                Returns:
         | 
| 574 606 | 
             
                    True if logged/queued successfully
         | 
| 575 607 | 
             
                """
         | 
| @@ -580,10 +612,12 @@ def log_response_async( | |
| 580 612 | 
             
            # Cleanup on module unload
         | 
| 581 613 | 
             
            import atexit
         | 
| 582 614 |  | 
| 615 | 
            +
             | 
| 583 616 | 
             
            def _cleanup():
         | 
| 584 617 | 
             
                """Cleanup function called on exit."""
         | 
| 585 618 | 
             
                global _logger_instance
         | 
| 586 619 | 
             
                if _logger_instance:
         | 
| 587 620 | 
             
                    _logger_instance.shutdown()
         | 
| 588 621 |  | 
| 589 | 
            -
             | 
| 622 | 
            +
             | 
| 623 | 
            +
            atexit.register(_cleanup)
         |