claude-mpm 5.6.23__py3-none-any.whl → 5.6.72__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/auth/__init__.py +35 -0
- claude_mpm/auth/callback_server.py +328 -0
- claude_mpm/auth/models.py +104 -0
- claude_mpm/auth/oauth_manager.py +266 -0
- claude_mpm/auth/providers/__init__.py +12 -0
- claude_mpm/auth/providers/base.py +165 -0
- claude_mpm/auth/providers/google.py +261 -0
- claude_mpm/auth/token_storage.py +252 -0
- claude_mpm/cli/commands/commander.py +6 -6
- claude_mpm/cli/commands/mcp.py +29 -17
- claude_mpm/cli/commands/mcp_command_router.py +39 -0
- claude_mpm/cli/commands/mcp_service_commands.py +304 -0
- claude_mpm/cli/commands/oauth.py +481 -0
- claude_mpm/cli/executor.py +9 -0
- claude_mpm/cli/helpers.py +1 -1
- claude_mpm/cli/parsers/base_parser.py +13 -0
- claude_mpm/cli/parsers/mcp_parser.py +79 -0
- claude_mpm/cli/parsers/oauth_parser.py +165 -0
- claude_mpm/cli/startup.py +150 -33
- claude_mpm/cli/startup_display.py +3 -2
- claude_mpm/commander/chat/cli.py +5 -2
- claude_mpm/commander/chat/commands.py +42 -16
- claude_mpm/commander/chat/repl.py +1581 -70
- claude_mpm/commander/events/manager.py +61 -1
- claude_mpm/commander/frameworks/base.py +87 -0
- claude_mpm/commander/frameworks/mpm.py +9 -14
- claude_mpm/commander/git/__init__.py +5 -0
- claude_mpm/commander/git/worktree_manager.py +212 -0
- claude_mpm/commander/instance_manager.py +428 -13
- claude_mpm/commander/models/events.py +6 -0
- claude_mpm/commander/persistence/state_store.py +95 -1
- claude_mpm/commander/tmux_orchestrator.py +3 -2
- claude_mpm/constants.py +5 -0
- claude_mpm/core/hook_manager.py +2 -1
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/output_style_manager.py +5 -2
- claude_mpm/core/socketio_pool.py +34 -10
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +1 -1
- claude_mpm/hooks/claude_hooks/event_handlers.py +206 -94
- claude_mpm/hooks/claude_hooks/hook_handler.py +115 -32
- claude_mpm/hooks/claude_hooks/installer.py +175 -51
- claude_mpm/hooks/claude_hooks/memory_integration.py +1 -1
- claude_mpm/hooks/claude_hooks/response_tracking.py +1 -1
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +2 -2
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +2 -2
- claude_mpm/hooks/claude_hooks/services/container.py +326 -0
- claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +2 -2
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +2 -2
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +6 -6
- claude_mpm/init.py +21 -14
- claude_mpm/mcp/__init__.py +9 -0
- claude_mpm/mcp/google_workspace_server.py +610 -0
- claude_mpm/scripts/claude-hook-handler.sh +3 -3
- claude_mpm/services/command_deployment_service.py +44 -26
- claude_mpm/services/hook_installer_service.py +77 -8
- claude_mpm/services/mcp_config_manager.py +99 -19
- claude_mpm/services/mcp_service_registry.py +294 -0
- claude_mpm/services/monitor/server.py +6 -1
- {claude_mpm-5.6.23.dist-info → claude_mpm-5.6.72.dist-info}/METADATA +24 -1
- {claude_mpm-5.6.23.dist-info → claude_mpm-5.6.72.dist-info}/RECORD +80 -60
- {claude_mpm-5.6.23.dist-info → claude_mpm-5.6.72.dist-info}/WHEEL +1 -1
- {claude_mpm-5.6.23.dist-info → claude_mpm-5.6.72.dist-info}/entry_points.txt +2 -0
- {claude_mpm-5.6.23.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.6.23.dist-info → claude_mpm-5.6.72.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.6.23.dist-info → claude_mpm-5.6.72.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
"""Dependency Injection container for hook handler services.
|
|
2
|
+
|
|
3
|
+
This module provides a DI container that manages service instantiation,
|
|
4
|
+
lazy initialization, and service overriding for testing.
|
|
5
|
+
|
|
6
|
+
WHY DI Container:
|
|
7
|
+
- Centralized service management
|
|
8
|
+
- Lazy initialization reduces startup overhead
|
|
9
|
+
- Easy testing through service overriding
|
|
10
|
+
- Clear dependency graph
|
|
11
|
+
- Thread-safe singleton pattern
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import threading
|
|
15
|
+
from typing import Any, Optional, TypeVar
|
|
16
|
+
|
|
17
|
+
# Import service protocols for type hints
|
|
18
|
+
from .protocols import (
|
|
19
|
+
IAutoPauseHandler,
|
|
20
|
+
IConnectionManager,
|
|
21
|
+
IDuplicateDetector,
|
|
22
|
+
IEventHandlers,
|
|
23
|
+
IMemoryHookManager,
|
|
24
|
+
IResponseTrackingManager,
|
|
25
|
+
IStateManager,
|
|
26
|
+
ISubagentProcessor,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
T = TypeVar("T")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class HookServiceContainer:
|
|
33
|
+
"""Dependency injection container for hook handler services.
|
|
34
|
+
|
|
35
|
+
Features:
|
|
36
|
+
- Lazy initialization of services
|
|
37
|
+
- Thread-safe singleton pattern
|
|
38
|
+
- Service overriding for testing
|
|
39
|
+
- Automatic dependency resolution
|
|
40
|
+
|
|
41
|
+
Usage:
|
|
42
|
+
# Normal usage (services created lazily)
|
|
43
|
+
container = HookServiceContainer()
|
|
44
|
+
state_manager = container.get_state_manager()
|
|
45
|
+
|
|
46
|
+
# Testing usage (override services)
|
|
47
|
+
container = HookServiceContainer()
|
|
48
|
+
container.override_state_manager(mock_state_manager)
|
|
49
|
+
state_manager = container.get_state_manager() # Returns mock
|
|
50
|
+
|
|
51
|
+
# Reset overrides
|
|
52
|
+
container.reset_overrides()
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
# Thread-safe singleton
|
|
56
|
+
_instance: Optional["HookServiceContainer"] = None
|
|
57
|
+
_lock = threading.Lock()
|
|
58
|
+
|
|
59
|
+
def __new__(cls) -> "HookServiceContainer":
|
|
60
|
+
"""Thread-safe singleton pattern."""
|
|
61
|
+
if cls._instance is None:
|
|
62
|
+
with cls._lock:
|
|
63
|
+
# Double-check locking pattern
|
|
64
|
+
if cls._instance is None:
|
|
65
|
+
cls._instance = super().__new__(cls)
|
|
66
|
+
cls._instance._initialized = False
|
|
67
|
+
return cls._instance
|
|
68
|
+
|
|
69
|
+
def __init__(self) -> None:
|
|
70
|
+
"""Initialize container if not already initialized."""
|
|
71
|
+
if getattr(self, "_initialized", False):
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
# Service instances (lazily initialized)
|
|
75
|
+
self._state_manager: Optional[IStateManager] = None
|
|
76
|
+
self._connection_manager: Optional[IConnectionManager] = None
|
|
77
|
+
self._duplicate_detector: Optional[IDuplicateDetector] = None
|
|
78
|
+
self._response_tracking_manager: Optional[IResponseTrackingManager] = None
|
|
79
|
+
self._memory_hook_manager: Optional[IMemoryHookManager] = None
|
|
80
|
+
self._subagent_processor: Optional[ISubagentProcessor] = None
|
|
81
|
+
self._auto_pause_handler: Optional[IAutoPauseHandler] = None
|
|
82
|
+
self._event_handlers: Optional[IEventHandlers] = None
|
|
83
|
+
|
|
84
|
+
# Override factories for testing
|
|
85
|
+
self._overrides: dict[str, Any] = {}
|
|
86
|
+
|
|
87
|
+
# Lock for lazy initialization
|
|
88
|
+
self._init_lock = threading.Lock()
|
|
89
|
+
|
|
90
|
+
self._initialized = True
|
|
91
|
+
|
|
92
|
+
# =========================================================================
|
|
93
|
+
# Lazy Service Getters
|
|
94
|
+
# =========================================================================
|
|
95
|
+
|
|
96
|
+
def get_state_manager(self) -> IStateManager:
|
|
97
|
+
"""Get or create StateManagerService instance."""
|
|
98
|
+
if "state_manager" in self._overrides:
|
|
99
|
+
return self._overrides["state_manager"]
|
|
100
|
+
|
|
101
|
+
if self._state_manager is None:
|
|
102
|
+
with self._init_lock:
|
|
103
|
+
if self._state_manager is None:
|
|
104
|
+
from claude_mpm.hooks.claude_hooks.services.state_manager import (
|
|
105
|
+
StateManagerService,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
self._state_manager = StateManagerService()
|
|
109
|
+
return self._state_manager
|
|
110
|
+
|
|
111
|
+
def get_connection_manager(self) -> IConnectionManager:
|
|
112
|
+
"""Get or create ConnectionManagerService instance."""
|
|
113
|
+
if "connection_manager" in self._overrides:
|
|
114
|
+
return self._overrides["connection_manager"]
|
|
115
|
+
|
|
116
|
+
if self._connection_manager is None:
|
|
117
|
+
with self._init_lock:
|
|
118
|
+
if self._connection_manager is None:
|
|
119
|
+
from claude_mpm.hooks.claude_hooks.services.connection_manager_http import (
|
|
120
|
+
ConnectionManagerService,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
self._connection_manager = ConnectionManagerService()
|
|
124
|
+
return self._connection_manager
|
|
125
|
+
|
|
126
|
+
def get_duplicate_detector(self) -> IDuplicateDetector:
|
|
127
|
+
"""Get or create DuplicateEventDetector instance."""
|
|
128
|
+
if "duplicate_detector" in self._overrides:
|
|
129
|
+
return self._overrides["duplicate_detector"]
|
|
130
|
+
|
|
131
|
+
if self._duplicate_detector is None:
|
|
132
|
+
with self._init_lock:
|
|
133
|
+
if self._duplicate_detector is None:
|
|
134
|
+
from claude_mpm.hooks.claude_hooks.services.duplicate_detector import (
|
|
135
|
+
DuplicateEventDetector,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
self._duplicate_detector = DuplicateEventDetector()
|
|
139
|
+
return self._duplicate_detector
|
|
140
|
+
|
|
141
|
+
def get_response_tracking_manager(self) -> IResponseTrackingManager:
|
|
142
|
+
"""Get or create ResponseTrackingManager instance."""
|
|
143
|
+
if "response_tracking_manager" in self._overrides:
|
|
144
|
+
return self._overrides["response_tracking_manager"]
|
|
145
|
+
|
|
146
|
+
if self._response_tracking_manager is None:
|
|
147
|
+
with self._init_lock:
|
|
148
|
+
if self._response_tracking_manager is None:
|
|
149
|
+
from claude_mpm.hooks.claude_hooks.response_tracking import (
|
|
150
|
+
ResponseTrackingManager,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
self._response_tracking_manager = ResponseTrackingManager()
|
|
154
|
+
return self._response_tracking_manager
|
|
155
|
+
|
|
156
|
+
def get_memory_hook_manager(self) -> IMemoryHookManager:
|
|
157
|
+
"""Get or create MemoryHookManager instance."""
|
|
158
|
+
if "memory_hook_manager" in self._overrides:
|
|
159
|
+
return self._overrides["memory_hook_manager"]
|
|
160
|
+
|
|
161
|
+
if self._memory_hook_manager is None:
|
|
162
|
+
with self._init_lock:
|
|
163
|
+
if self._memory_hook_manager is None:
|
|
164
|
+
from claude_mpm.hooks.claude_hooks.memory_integration import (
|
|
165
|
+
MemoryHookManager,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
self._memory_hook_manager = MemoryHookManager()
|
|
169
|
+
return self._memory_hook_manager
|
|
170
|
+
|
|
171
|
+
def get_auto_pause_handler(self) -> Optional[IAutoPauseHandler]:
|
|
172
|
+
"""Get or create AutoPauseHandler instance.
|
|
173
|
+
|
|
174
|
+
Returns None if initialization fails (auto-pause is optional).
|
|
175
|
+
"""
|
|
176
|
+
if "auto_pause_handler" in self._overrides:
|
|
177
|
+
return self._overrides["auto_pause_handler"]
|
|
178
|
+
|
|
179
|
+
if self._auto_pause_handler is None:
|
|
180
|
+
with self._init_lock:
|
|
181
|
+
if self._auto_pause_handler is None:
|
|
182
|
+
try:
|
|
183
|
+
from claude_mpm.hooks.claude_hooks.auto_pause_handler import (
|
|
184
|
+
AutoPauseHandler,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
self._auto_pause_handler = AutoPauseHandler()
|
|
188
|
+
except Exception:
|
|
189
|
+
# Auto-pause is optional
|
|
190
|
+
self._auto_pause_handler = None
|
|
191
|
+
return self._auto_pause_handler
|
|
192
|
+
|
|
193
|
+
def get_subagent_processor(
|
|
194
|
+
self,
|
|
195
|
+
state_manager: Optional[IStateManager] = None,
|
|
196
|
+
response_tracking_manager: Optional[IResponseTrackingManager] = None,
|
|
197
|
+
connection_manager: Optional[IConnectionManager] = None,
|
|
198
|
+
) -> ISubagentProcessor:
|
|
199
|
+
"""Get or create SubagentResponseProcessor instance.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
state_manager: Optional override for state manager
|
|
203
|
+
response_tracking_manager: Optional override for response tracking
|
|
204
|
+
connection_manager: Optional override for connection manager
|
|
205
|
+
"""
|
|
206
|
+
if "subagent_processor" in self._overrides:
|
|
207
|
+
return self._overrides["subagent_processor"]
|
|
208
|
+
|
|
209
|
+
if self._subagent_processor is None:
|
|
210
|
+
with self._init_lock:
|
|
211
|
+
if self._subagent_processor is None:
|
|
212
|
+
from claude_mpm.hooks.claude_hooks.services.subagent_processor import (
|
|
213
|
+
SubagentResponseProcessor,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Use provided dependencies or get from container
|
|
217
|
+
sm = state_manager or self.get_state_manager()
|
|
218
|
+
rtm = (
|
|
219
|
+
response_tracking_manager
|
|
220
|
+
or self.get_response_tracking_manager()
|
|
221
|
+
)
|
|
222
|
+
cm = connection_manager or self.get_connection_manager()
|
|
223
|
+
|
|
224
|
+
self._subagent_processor = SubagentResponseProcessor(sm, rtm, cm)
|
|
225
|
+
return self._subagent_processor
|
|
226
|
+
|
|
227
|
+
def get_event_handlers(self, hook_handler: Any = None) -> IEventHandlers:
|
|
228
|
+
"""Get or create EventHandlers instance.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
hook_handler: The ClaudeHookHandler instance for backward compatibility.
|
|
232
|
+
In the future, this will be replaced with proper DI.
|
|
233
|
+
"""
|
|
234
|
+
if "event_handlers" in self._overrides:
|
|
235
|
+
return self._overrides["event_handlers"]
|
|
236
|
+
|
|
237
|
+
if self._event_handlers is None:
|
|
238
|
+
with self._init_lock:
|
|
239
|
+
if self._event_handlers is None:
|
|
240
|
+
from claude_mpm.hooks.claude_hooks.event_handlers import (
|
|
241
|
+
EventHandlers,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if hook_handler is None:
|
|
245
|
+
raise ValueError(
|
|
246
|
+
"hook_handler is required to create EventHandlers"
|
|
247
|
+
)
|
|
248
|
+
self._event_handlers = EventHandlers(hook_handler)
|
|
249
|
+
return self._event_handlers
|
|
250
|
+
|
|
251
|
+
# =========================================================================
|
|
252
|
+
# Service Override Methods (for testing)
|
|
253
|
+
# =========================================================================
|
|
254
|
+
|
|
255
|
+
def override_state_manager(self, service: IStateManager) -> None:
|
|
256
|
+
"""Override state manager with a mock or custom implementation."""
|
|
257
|
+
self._overrides["state_manager"] = service
|
|
258
|
+
|
|
259
|
+
def override_connection_manager(self, service: IConnectionManager) -> None:
|
|
260
|
+
"""Override connection manager with a mock or custom implementation."""
|
|
261
|
+
self._overrides["connection_manager"] = service
|
|
262
|
+
|
|
263
|
+
def override_duplicate_detector(self, service: IDuplicateDetector) -> None:
|
|
264
|
+
"""Override duplicate detector with a mock or custom implementation."""
|
|
265
|
+
self._overrides["duplicate_detector"] = service
|
|
266
|
+
|
|
267
|
+
def override_response_tracking_manager(
|
|
268
|
+
self, service: IResponseTrackingManager
|
|
269
|
+
) -> None:
|
|
270
|
+
"""Override response tracking manager with a mock or custom implementation."""
|
|
271
|
+
self._overrides["response_tracking_manager"] = service
|
|
272
|
+
|
|
273
|
+
def override_memory_hook_manager(self, service: IMemoryHookManager) -> None:
|
|
274
|
+
"""Override memory hook manager with a mock or custom implementation."""
|
|
275
|
+
self._overrides["memory_hook_manager"] = service
|
|
276
|
+
|
|
277
|
+
def override_subagent_processor(self, service: ISubagentProcessor) -> None:
|
|
278
|
+
"""Override subagent processor with a mock or custom implementation."""
|
|
279
|
+
self._overrides["subagent_processor"] = service
|
|
280
|
+
|
|
281
|
+
def override_auto_pause_handler(self, service: Optional[IAutoPauseHandler]) -> None:
|
|
282
|
+
"""Override auto-pause handler with a mock or custom implementation."""
|
|
283
|
+
self._overrides["auto_pause_handler"] = service
|
|
284
|
+
|
|
285
|
+
def override_event_handlers(self, service: IEventHandlers) -> None:
|
|
286
|
+
"""Override event handlers with a mock or custom implementation."""
|
|
287
|
+
self._overrides["event_handlers"] = service
|
|
288
|
+
|
|
289
|
+
def reset_overrides(self) -> None:
|
|
290
|
+
"""Reset all service overrides."""
|
|
291
|
+
self._overrides.clear()
|
|
292
|
+
|
|
293
|
+
def reset_all(self) -> None:
|
|
294
|
+
"""Reset all services and overrides.
|
|
295
|
+
|
|
296
|
+
Useful for testing to ensure clean state between tests.
|
|
297
|
+
"""
|
|
298
|
+
self._state_manager = None
|
|
299
|
+
self._connection_manager = None
|
|
300
|
+
self._duplicate_detector = None
|
|
301
|
+
self._response_tracking_manager = None
|
|
302
|
+
self._memory_hook_manager = None
|
|
303
|
+
self._subagent_processor = None
|
|
304
|
+
self._auto_pause_handler = None
|
|
305
|
+
self._event_handlers = None
|
|
306
|
+
self._overrides.clear()
|
|
307
|
+
|
|
308
|
+
@classmethod
|
|
309
|
+
def reset_singleton(cls) -> None:
|
|
310
|
+
"""Reset the singleton instance.
|
|
311
|
+
|
|
312
|
+
USE WITH CAUTION: This is primarily for testing.
|
|
313
|
+
"""
|
|
314
|
+
with cls._lock:
|
|
315
|
+
if cls._instance is not None:
|
|
316
|
+
cls._instance.reset_all()
|
|
317
|
+
cls._instance = None
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def get_container() -> HookServiceContainer:
|
|
321
|
+
"""Get the global HookServiceContainer singleton.
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
The singleton HookServiceContainer instance.
|
|
325
|
+
"""
|
|
326
|
+
return HookServiceContainer()
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
"""Protocol definitions for hook handler services.
|
|
2
|
+
|
|
3
|
+
This module defines Protocol classes for all hook handler services,
|
|
4
|
+
enabling type-safe dependency injection and easy testing through
|
|
5
|
+
protocol-based duck typing.
|
|
6
|
+
|
|
7
|
+
WHY Protocol-based DI:
|
|
8
|
+
- Enables loose coupling between components
|
|
9
|
+
- Allows easy mocking in tests without monkey-patching
|
|
10
|
+
- Provides clear interface contracts
|
|
11
|
+
- Supports static type checking with mypy
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Any, Callable, Optional, Protocol, runtime_checkable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class IStateManager(Protocol):
|
|
19
|
+
"""Protocol for state management service.
|
|
20
|
+
|
|
21
|
+
Manages:
|
|
22
|
+
- Agent delegation tracking
|
|
23
|
+
- Git branch caching
|
|
24
|
+
- Session state management
|
|
25
|
+
- Cleanup of old entries
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
active_delegations: dict
|
|
29
|
+
delegation_history: Any # deque
|
|
30
|
+
delegation_requests: dict
|
|
31
|
+
pending_prompts: dict
|
|
32
|
+
events_processed: int
|
|
33
|
+
|
|
34
|
+
def track_delegation(
|
|
35
|
+
self, session_id: str, agent_type: str, request_data: Optional[dict] = None
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Track a new agent delegation with optional request data."""
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
def get_delegation_agent_type(self, session_id: str) -> str:
|
|
41
|
+
"""Get the agent type for a session's active delegation."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
def cleanup_old_entries(self) -> None:
|
|
45
|
+
"""Clean up old entries to prevent memory growth."""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
def get_git_branch(self, working_dir: Optional[str] = None) -> str:
|
|
49
|
+
"""Get git branch for the given directory with caching."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def find_matching_request(self, session_id: str) -> Optional[dict]:
|
|
53
|
+
"""Find matching request data for a session, with fuzzy matching fallback."""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
def remove_request(self, session_id: str) -> None:
|
|
57
|
+
"""Remove request data for a session."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
def increment_events_processed(self) -> bool:
|
|
61
|
+
"""Increment events processed counter and return True if cleanup is needed."""
|
|
62
|
+
...
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@runtime_checkable
|
|
66
|
+
class IConnectionManager(Protocol):
|
|
67
|
+
"""Protocol for connection management service.
|
|
68
|
+
|
|
69
|
+
Handles:
|
|
70
|
+
- HTTP-based event emission to dashboard
|
|
71
|
+
- Event queuing and batching
|
|
72
|
+
- Connection state management
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def emit_event(self, namespace: str, event: str, data: dict) -> None:
|
|
76
|
+
"""Emit an event through HTTP to the dashboard."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
def cleanup(self) -> None:
|
|
80
|
+
"""Clean up any resources."""
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@runtime_checkable
|
|
85
|
+
class IDuplicateDetector(Protocol):
|
|
86
|
+
"""Protocol for duplicate event detection service.
|
|
87
|
+
|
|
88
|
+
Detects:
|
|
89
|
+
- Duplicate events within a time window
|
|
90
|
+
- Rapid-fire events that should be deduplicated
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
def is_duplicate(self, event: dict) -> bool:
|
|
94
|
+
"""Check if an event is a duplicate of a recent event."""
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
def generate_event_key(self, event: dict) -> str:
|
|
98
|
+
"""Generate a unique key for an event for deduplication."""
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@runtime_checkable
|
|
103
|
+
class IResponseTrackingManager(Protocol):
|
|
104
|
+
"""Protocol for response tracking management.
|
|
105
|
+
|
|
106
|
+
Manages:
|
|
107
|
+
- Response tracking enablement
|
|
108
|
+
- Comprehensive response logging
|
|
109
|
+
- Agent response correlation
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
response_tracking_enabled: bool
|
|
113
|
+
response_tracker: Optional[Any]
|
|
114
|
+
track_all_interactions: bool
|
|
115
|
+
auto_pause_handler: Optional[Any]
|
|
116
|
+
|
|
117
|
+
def track_agent_response(
|
|
118
|
+
self,
|
|
119
|
+
session_id: str,
|
|
120
|
+
agent_type: str,
|
|
121
|
+
event: dict,
|
|
122
|
+
delegation_requests: dict,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Track an agent response for logging."""
|
|
125
|
+
...
|
|
126
|
+
|
|
127
|
+
def track_stop_response(
|
|
128
|
+
self,
|
|
129
|
+
event: dict,
|
|
130
|
+
session_id: str,
|
|
131
|
+
metadata: dict,
|
|
132
|
+
pending_prompts: dict,
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Track a stop event response."""
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
def track_assistant_response(
|
|
138
|
+
self,
|
|
139
|
+
event: dict,
|
|
140
|
+
pending_prompts: dict,
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Track an assistant response."""
|
|
143
|
+
...
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@runtime_checkable
|
|
147
|
+
class IMemoryHookManager(Protocol):
|
|
148
|
+
"""Protocol for memory hook management.
|
|
149
|
+
|
|
150
|
+
Manages:
|
|
151
|
+
- Pre/post delegation memory hooks
|
|
152
|
+
- Memory field processing
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
def trigger_pre_delegation_hook(
|
|
156
|
+
self, agent_type: str, tool_input: dict, session_id: str
|
|
157
|
+
) -> None:
|
|
158
|
+
"""Trigger memory hooks before delegation."""
|
|
159
|
+
...
|
|
160
|
+
|
|
161
|
+
def trigger_post_delegation_hook(
|
|
162
|
+
self, agent_type: str, event: dict, session_id: str
|
|
163
|
+
) -> None:
|
|
164
|
+
"""Trigger memory hooks after delegation."""
|
|
165
|
+
...
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@runtime_checkable
|
|
169
|
+
class ISubagentProcessor(Protocol):
|
|
170
|
+
"""Protocol for subagent response processing.
|
|
171
|
+
|
|
172
|
+
Handles:
|
|
173
|
+
- SubagentStop event processing
|
|
174
|
+
- Structured response extraction
|
|
175
|
+
- Response tracking and correlation
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
def process_subagent_stop(self, event: dict) -> None:
|
|
179
|
+
"""Handle subagent stop events."""
|
|
180
|
+
...
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@runtime_checkable
|
|
184
|
+
class IAutoPauseHandler(Protocol):
|
|
185
|
+
"""Protocol for auto-pause functionality.
|
|
186
|
+
|
|
187
|
+
Manages:
|
|
188
|
+
- Session pause tracking
|
|
189
|
+
- Usage threshold monitoring
|
|
190
|
+
- Session state capture
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
def is_pause_active(self) -> bool:
|
|
194
|
+
"""Check if auto-pause is currently active."""
|
|
195
|
+
...
|
|
196
|
+
|
|
197
|
+
def on_user_message(self, message: str) -> None:
|
|
198
|
+
"""Record a user message for auto-pause tracking."""
|
|
199
|
+
...
|
|
200
|
+
|
|
201
|
+
def on_tool_call(self, tool_name: str, tool_input: dict) -> None:
|
|
202
|
+
"""Record a tool call for auto-pause tracking."""
|
|
203
|
+
...
|
|
204
|
+
|
|
205
|
+
def on_assistant_response(self, response: str) -> None:
|
|
206
|
+
"""Record an assistant response for auto-pause tracking."""
|
|
207
|
+
...
|
|
208
|
+
|
|
209
|
+
def on_usage_update(self, usage: dict) -> Optional[str]:
|
|
210
|
+
"""Update usage metrics and return threshold crossed if any."""
|
|
211
|
+
...
|
|
212
|
+
|
|
213
|
+
def emit_threshold_warning(self, threshold: str) -> str:
|
|
214
|
+
"""Emit a warning for a crossed threshold."""
|
|
215
|
+
...
|
|
216
|
+
|
|
217
|
+
def on_session_end(self) -> Optional[Any]:
|
|
218
|
+
"""Finalize the current auto-pause session."""
|
|
219
|
+
...
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@runtime_checkable
|
|
223
|
+
class IEventHandlers(Protocol):
|
|
224
|
+
"""Protocol for event handler collection.
|
|
225
|
+
|
|
226
|
+
Provides handlers for:
|
|
227
|
+
- UserPromptSubmit
|
|
228
|
+
- PreToolUse
|
|
229
|
+
- PostToolUse
|
|
230
|
+
- Notification
|
|
231
|
+
- Stop
|
|
232
|
+
- SubagentStop
|
|
233
|
+
- SubagentStart
|
|
234
|
+
- SessionStart
|
|
235
|
+
- AssistantResponse
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
def handle_user_prompt_fast(self, event: dict) -> None:
|
|
239
|
+
"""Handle user prompt with comprehensive data capture."""
|
|
240
|
+
...
|
|
241
|
+
|
|
242
|
+
def handle_pre_tool_fast(self, event: dict) -> Optional[dict]:
|
|
243
|
+
"""Handle pre-tool use with comprehensive data capture."""
|
|
244
|
+
...
|
|
245
|
+
|
|
246
|
+
def handle_post_tool_fast(self, event: dict) -> None:
|
|
247
|
+
"""Handle post-tool use with comprehensive data capture."""
|
|
248
|
+
...
|
|
249
|
+
|
|
250
|
+
def handle_notification_fast(self, event: dict) -> None:
|
|
251
|
+
"""Handle notification events from Claude."""
|
|
252
|
+
...
|
|
253
|
+
|
|
254
|
+
def handle_stop_fast(self, event: dict) -> None:
|
|
255
|
+
"""Handle stop events when Claude processing stops."""
|
|
256
|
+
...
|
|
257
|
+
|
|
258
|
+
def handle_subagent_stop_fast(self, event: dict) -> None:
|
|
259
|
+
"""Handle subagent stop events."""
|
|
260
|
+
...
|
|
261
|
+
|
|
262
|
+
def handle_subagent_start_fast(self, event: dict) -> None:
|
|
263
|
+
"""Handle SubagentStart events."""
|
|
264
|
+
...
|
|
265
|
+
|
|
266
|
+
def handle_session_start_fast(self, event: dict) -> None:
|
|
267
|
+
"""Handle session start events."""
|
|
268
|
+
...
|
|
269
|
+
|
|
270
|
+
def handle_assistant_response(self, event: dict) -> None:
|
|
271
|
+
"""Handle assistant response events."""
|
|
272
|
+
...
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Type alias for log function
|
|
276
|
+
LogFunction = Callable[[str], None]
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@runtime_checkable
|
|
280
|
+
class ILogManager(Protocol):
|
|
281
|
+
"""Protocol for log manager service (optional dependency).
|
|
282
|
+
|
|
283
|
+
Used for logging agent prompts and responses.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
async def log_prompt(
|
|
287
|
+
self, source: str, content: str, metadata: dict
|
|
288
|
+
) -> Optional[Any]:
|
|
289
|
+
"""Log a prompt to the log manager."""
|
|
290
|
+
...
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@runtime_checkable
|
|
294
|
+
class IDelegationDetector(Protocol):
|
|
295
|
+
"""Protocol for delegation pattern detector (optional dependency).
|
|
296
|
+
|
|
297
|
+
Used for detecting delegation anti-patterns in responses.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
def detect_user_delegation(self, text: str) -> list[dict]:
|
|
301
|
+
"""Detect delegation patterns in text."""
|
|
302
|
+
...
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@runtime_checkable
|
|
306
|
+
class IEventLog(Protocol):
|
|
307
|
+
"""Protocol for event log service (optional dependency).
|
|
308
|
+
|
|
309
|
+
Used for logging PM violations and other events.
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
def append_event(
|
|
313
|
+
self,
|
|
314
|
+
event_type: str,
|
|
315
|
+
payload: dict,
|
|
316
|
+
status: str = "pending",
|
|
317
|
+
) -> None:
|
|
318
|
+
"""Append an event to the log."""
|
|
319
|
+
...
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@runtime_checkable
|
|
323
|
+
class IConfig(Protocol):
|
|
324
|
+
"""Protocol for configuration service (optional dependency)."""
|
|
325
|
+
|
|
326
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
327
|
+
"""Get a configuration value."""
|
|
328
|
+
...
|
|
@@ -33,8 +33,8 @@ except ImportError:
|
|
|
33
33
|
QUICK_TIMEOUT = 2.0
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
# Debug mode
|
|
37
|
-
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "
|
|
36
|
+
# Debug mode - disabled by default to prevent logging overhead in production
|
|
37
|
+
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
class StateManagerService:
|
|
@@ -22,8 +22,8 @@ except ImportError:
|
|
|
22
22
|
pass # Silent fallback
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
# Debug mode
|
|
26
|
-
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "
|
|
25
|
+
# Debug mode - disabled by default to prevent logging overhead in production
|
|
26
|
+
DEBUG = os.environ.get("CLAUDE_MPM_HOOK_DEBUG", "false").lower() == "true"
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class SubagentResponseProcessor:
|