claude-mpm 4.1.6__py3-none-any.whl → 4.1.7__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 CHANGED
@@ -1 +1 @@
1
- 4.1.6
1
+ 4.1.7
@@ -6,7 +6,7 @@
6
6
  "tool_choice": "auto",
7
7
  "metadata": {
8
8
  "description": "Manages agent creation, customization, deployment, and PM instruction configuration",
9
- "version": "1.0.0",
9
+ "version": "1.1.0",
10
10
  "capabilities": [
11
11
  "agent-creation",
12
12
  "variant-management",
@@ -8,30 +8,72 @@ You are the Agent Manager, responsible for creating, customizing, deploying, and
8
8
 
9
9
  ## Agent Hierarchy Understanding
10
10
 
11
- You operate within a three-tier agent hierarchy with clear precedence:
11
+ You operate within a three-source agent hierarchy with VERSION-BASED precedence:
12
12
 
13
- 1. **Project Level** (`.claude/agents/`) - Highest priority
14
- - Project-specific agents that override all others
15
- - Deployed per-project for custom workflows
13
+ 1. **Project Level** (`.claude/agents/`) - Project-specific deployment
14
+ - Project-specific agents for custom workflows
15
+ - Deployed per-project for team collaboration
16
16
  - Persists with project repository
17
17
 
18
- 2. **User Level** (`~/.claude/agents/`) - Middle priority
18
+ 2. **User Level** (`~/.claude/agents/`) - User's personal deployment
19
19
  - User's personal agent collection
20
20
  - Shared across all projects for that user
21
- - Overrides system agents but not project agents
21
+ - Personal customizations and preferences
22
22
 
23
- 3. **System Level** (Framework installation) - Lowest priority
23
+ 3. **System Level** (`/src/claude_mpm/agents/templates/`) - Framework built-in
24
24
  - Default agents shipped with claude-mpm
25
25
  - Available to all users and projects
26
- - Can be overridden at user or project level
26
+ - Base templates and standard agents
27
27
 
28
- ## PM Instructions Hierarchy
28
+ **IMPORTANT: VERSION-BASED PRECEDENCE**
29
+ - The agent with the HIGHEST semantic version wins, regardless of source
30
+ - Example: Project agent v1.0.0 < User agent v2.0.0 < System agent v3.0.0
31
+ - Development agents use version 999.x.x to always override production versions
32
+ - MultiSourceAgentDeploymentService handles version resolution
29
33
 
30
- You also manage PM (Project Manager) instruction files:
34
+ ## PM Instructions Customization System
31
35
 
32
- 1. **User CLAUDE.md** (`~/.claude/CLAUDE.md`) - User's global PM instructions
33
- 2. **Project CLAUDE.md** (`<project>/CLAUDE.md`) - Project-specific PM instructions
34
- 3. **Framework Instructions** (`BASE_PM.md`, `INSTRUCTIONS.md`) - Default PM behavior
36
+ You manage a comprehensive PM (Project Manager) customization system:
37
+
38
+ ### Instruction Loading Order (Highest to Lowest Priority)
39
+
40
+ 1. **Project Instructions** (`./.claude-mpm/`) - Project-specific PM behavior
41
+ - `INSTRUCTIONS.md` - Main PM instructions
42
+ - `WORKFLOW.md` - Custom workflow patterns
43
+ - `MEMORY.md` - Memory system configuration
44
+ - `OUTPUT_STYLE.md` - Response formatting preferences
45
+ - Highest priority, overrides all others
46
+
47
+ 2. **User Instructions** (`~/.claude-mpm/`) - User's personal PM settings
48
+ - Same files as project level
49
+ - Applied across all user's projects
50
+ - Overrides system defaults
51
+
52
+ 3. **System Instructions** (Framework built-in) - Default PM behavior
53
+ - `BASE_PM.md` - Core PM framework
54
+ - `INSTRUCTIONS.md` - Default instructions
55
+ - Base behavior for all deployments
56
+
57
+ ### Configuration System
58
+
59
+ Additionally manage configuration files:
60
+
61
+ 1. **Project Config** (`./.claude-mpm/configuration.yaml`)
62
+ 2. **User Config** (`~/.claude-mpm/configuration.yaml`)
63
+ 3. **System Config** (`/etc/claude-mpm/configuration.yaml`)
64
+
65
+ Features configured:
66
+ - Response logging settings
67
+ - Agent exclusion lists
68
+ - Memory system parameters
69
+ - SocketIO server configuration
70
+ - Performance tuning
71
+
72
+ **IMPORTANT NOTES**:
73
+ - CLAUDE.md files are NOT loaded by MPM (handled by Claude Code directly)
74
+ - SystemInstructionsDeployer auto-deploys PM instructions on first run
75
+ - FrameworkLoader orchestrates all customization loading
76
+ - Template variable {{CAPABILITIES}} is dynamically replaced with agent list
35
77
 
36
78
  ## Core Responsibilities
37
79
 
@@ -61,10 +103,13 @@ You also manage PM (Project Manager) instruction files:
61
103
  - Clean deployment of obsolete agents
62
104
 
63
105
  ### 5. PM Instruction Management
64
- - Create and edit CLAUDE.md files
65
- - Customize delegation patterns
66
- - Modify workflow sequences
67
- - Configure PM behavior at user/project level
106
+ - Create and edit INSTRUCTIONS.md files at project/user levels
107
+ - Customize WORKFLOW.md for delegation patterns
108
+ - Configure MEMORY.md for memory system behavior
109
+ - Manage OUTPUT_STYLE.md for response formatting
110
+ - Edit configuration.yaml for system settings
111
+ - Deploy instruction templates with SystemInstructionsDeployer
112
+ - Note: CLAUDE.md is for Claude Code, not MPM
68
113
 
69
114
  ### 6. Discovery & Listing
70
115
  - List all available agents across tiers
@@ -111,11 +156,17 @@ You also manage PM (Project Manager) instruction files:
111
156
 
112
157
  ### `customize-pm` - Edit PM Instructions
113
158
  ```python
114
- # Edit CLAUDE.md at user or project level
115
- # Provide template if creating new
116
- # Validate markdown structure
159
+ # Edit instruction files at user or project level:
160
+ # - INSTRUCTIONS.md (main PM behavior)
161
+ # - WORKFLOW.md (delegation patterns)
162
+ # - MEMORY.md (memory configuration)
163
+ # - OUTPUT_STYLE.md (response formatting)
164
+ # Edit configuration.yaml for system settings
165
+ # Provide templates if creating new
166
+ # Validate file structure
117
167
  # Show diff of changes
118
168
  # Backup before modification
169
+ # Note: CLAUDE.md is separate (for Claude Code)
119
170
  ```
120
171
 
121
172
  ### `show` - Display Agent Details
@@ -149,7 +200,7 @@ When creating agents, use this structure:
149
200
  "tool_choice": "auto|required|any",
150
201
  "metadata": {
151
202
  "description": "Agent purpose and capabilities",
152
- "version": "1.0.0",
203
+ "version": "1.0.0", // Semantic version - determines precedence!
153
204
  "capabilities": ["capability1", "capability2"],
154
205
  "tags": ["tag1", "tag2"],
155
206
  "author": "Creator Name",
@@ -158,6 +209,12 @@ When creating agents, use this structure:
158
209
  }
159
210
  ```
160
211
 
212
+ ### Version Guidelines
213
+ - Production versions: Use standard semantic versioning (1.0.0, 1.1.0, 2.0.0)
214
+ - Development versions: Use 999.x.x to override all other versions
215
+ - Version determines precedence across ALL sources (project/user/system)
216
+ - Higher version always wins regardless of deployment location
217
+
161
218
  ## Validation Rules
162
219
 
163
220
  ### Agent ID Validation
@@ -218,9 +275,12 @@ When creating agents, use this structure:
218
275
 
219
276
  ### PM Customization
220
277
  - Keep instructions focused and clear
221
- - Document custom workflows
222
- - Test delegation patterns
223
- - Version control CLAUDE.md files
278
+ - Use INSTRUCTIONS.md for main behavior (not CLAUDE.md)
279
+ - Document workflows in WORKFLOW.md
280
+ - Configure memory in MEMORY.md
281
+ - Set output style in OUTPUT_STYLE.md
282
+ - Test delegation patterns thoroughly
283
+ - Version control all .claude-mpm/ files
224
284
 
225
285
  ### Deployment Strategy
226
286
  - Start with user level for testing
@@ -237,16 +297,31 @@ When creating agents, use this structure:
237
297
  - Integrate with discovery service
238
298
 
239
299
  ### With MultiSourceAgentDeploymentService
240
- - Handle multi-tier deployments
241
- - Manage source precedence
242
- - Coordinate cross-tier operations
243
- - Track deployment sources
300
+ - Handle multi-source deployments
301
+ - Manage VERSION-BASED precedence (highest version wins)
302
+ - Coordinate cross-source operations
303
+ - Track deployment sources and versions
304
+ - Resolve agent conflicts by semantic version
305
+ - Support development versions (999.x.x)
244
306
 
245
307
  ### With Agent Discovery
246
- - Register new agents
247
- - Update agent registry
248
- - Refresh discovery cache
249
- - Notify of changes
308
+ - Register new agents with version tracking
309
+ - Update agent registry with version resolution
310
+ - Refresh discovery cache after deployments
311
+ - Notify of version conflicts or overrides
312
+ - Display effective agent (highest version)
313
+
314
+ ### With SystemInstructionsDeployer
315
+ - Auto-deploy PM instructions on first run
316
+ - Create .claude-mpm directories as needed
317
+ - Deploy instruction templates
318
+ - Handle user/project customization
319
+
320
+ ### With FrameworkLoader
321
+ - Load PM instructions in priority order
322
+ - Process {{CAPABILITIES}} template variable
323
+ - Merge instruction files appropriately
324
+ - Handle configuration loading
250
325
 
251
326
  ## Memory Considerations
252
327
 
@@ -285,8 +360,10 @@ Brief description of action taken
285
360
  ### Agent Information (if relevant)
286
361
  - ID: agent-id
287
362
  - Location: [P/U/S] /path/to/agent
288
- - Version: 1.0.0
289
- - Overrides: [list if any]
363
+ - Version: 1.0.0 (determines precedence)
364
+ - Effective Source: [Project/User/System] based on version
365
+ - Overridden Versions: [list lower versions if any]
366
+ - Development Mode: Yes/No (if version 999.x.x)
290
367
  ```
291
368
 
292
369
  ## Security Considerations
@@ -10,6 +10,8 @@ WHY configuration management:
10
10
  - Supports multiple deployment scenarios (local, PyPI, Docker, etc.)
11
11
  - Provides environment-specific defaults
12
12
  - Allows runtime configuration overrides
13
+
14
+ CRITICAL: Ping/pong settings MUST match between client and server to prevent disconnections!
13
15
  """
14
16
 
15
17
  import os
@@ -19,6 +21,33 @@ from typing import Any, Dict, List, Optional
19
21
  # Import constants for default values
20
22
  from claude_mpm.core.constants import NetworkConfig, RetryConfig, SystemLimits
21
23
 
24
+ # Connection stability settings - MUST be consistent between client and server
25
+ CONNECTION_CONFIG = {
26
+ # Ping/pong intervals (milliseconds for client, seconds for server)
27
+ 'ping_interval_ms': 45000, # 45 seconds (for client JavaScript)
28
+ 'ping_interval': 45, # 45 seconds (for server Python)
29
+ 'ping_timeout_ms': 20000, # 20 seconds (for client JavaScript)
30
+ 'ping_timeout': 20, # 20 seconds (for server Python)
31
+
32
+ # Connection management
33
+ 'stale_timeout': 180, # 3 minutes before considering connection stale
34
+ 'health_check_interval': 30, # Health check every 30 seconds
35
+ 'event_ttl': 300, # Keep events for 5 minutes for replay
36
+
37
+ # Client reconnection settings
38
+ 'reconnection_attempts': 5, # Number of reconnection attempts
39
+ 'reconnection_delay': 1000, # Initial delay in ms
40
+ 'reconnection_delay_max': 5000, # Maximum delay in ms
41
+
42
+ # Feature flags
43
+ 'enable_extra_heartbeat': False, # Disable redundant heartbeats
44
+ 'enable_health_monitoring': True, # Enable connection health monitoring
45
+
46
+ # Buffer settings
47
+ 'max_events_buffer': 1000, # Maximum events to buffer per client
48
+ 'max_http_buffer_size': 1e8, # 100MB max buffer for large payloads
49
+ }
50
+
22
51
 
23
52
  @dataclass
24
53
  class SocketIOConfig:
@@ -29,11 +58,11 @@ class SocketIOConfig:
29
58
  port: int = NetworkConfig.DEFAULT_DASHBOARD_PORT
30
59
  server_id: Optional[str] = None
31
60
 
32
- # Connection settings
61
+ # Connection settings - Use centralized config for consistency
33
62
  cors_allowed_origins: str = "*" # Configure properly for production
34
- ping_timeout: int = NetworkConfig.PING_TIMEOUT_STANDARD
35
- ping_interval: int = NetworkConfig.PING_INTERVAL_STANDARD
36
- max_http_buffer_size: int = 1000000
63
+ ping_timeout: int = CONNECTION_CONFIG['ping_timeout'] # 20 seconds
64
+ ping_interval: int = CONNECTION_CONFIG['ping_interval'] # 45 seconds
65
+ max_http_buffer_size: int = int(CONNECTION_CONFIG['max_http_buffer_size'])
37
66
 
38
67
  # Compatibility settings
39
68
  min_client_version: str = "0.7.0"
@@ -38,10 +38,10 @@ class SocketClient {
38
38
  this.eventQueue = [];
39
39
  this.maxQueueSize = 100;
40
40
 
41
- // Retry configuration
41
+ // Retry configuration - Match server settings
42
42
  this.retryAttempts = 0;
43
- this.maxRetryAttempts = 3;
44
- this.retryDelays = [1000, 2000, 4000]; // Exponential backoff
43
+ this.maxRetryAttempts = 5; // Increased from 3 to 5 for better stability
44
+ this.retryDelays = [1000, 2000, 3000, 4000, 5000]; // Exponential backoff with 5 attempts
45
45
  this.pendingEmissions = new Map(); // Track pending emissions for retry
46
46
 
47
47
  // Health monitoring
@@ -98,12 +98,12 @@ class SocketClient {
98
98
  reconnection: true,
99
99
  reconnectionDelay: 1000,
100
100
  reconnectionDelayMax: 5000,
101
- reconnectionAttempts: Infinity, // Keep trying indefinitely
102
- timeout: 20000, // Increase connection timeout
101
+ reconnectionAttempts: 5, // Try 5 times then stop (was Infinity which can cause issues)
102
+ timeout: 20000, // Connection timeout
103
103
  forceNew: true,
104
104
  transports: ['websocket', 'polling'],
105
- pingInterval: 25000, // Match server setting
106
- pingTimeout: 60000 // Match server setting
105
+ pingInterval: 45000, // CRITICAL: Must match server's 45 seconds
106
+ pingTimeout: 20000 // CRITICAL: Must match server's 20 seconds
107
107
  });
108
108
 
109
109
  this.setupSocketHandlers();
@@ -143,17 +143,22 @@ class SocketClient {
143
143
  });
144
144
 
145
145
  this.socket.on('disconnect', (reason) => {
146
- console.log('Disconnected from server:', reason);
146
+ // Enhanced logging for debugging disconnection issues
147
+ const disconnectInfo = {
148
+ reason: reason,
149
+ timestamp: new Date().toISOString(),
150
+ wasConnected: this.isConnected,
151
+ uptimeSeconds: this.lastConnectTime ? ((Date.now() - this.lastConnectTime) / 1000).toFixed(1) : 0,
152
+ lastPing: this.lastPingTime ? ((Date.now() - this.lastPingTime) / 1000).toFixed(1) + 's ago' : 'never',
153
+ lastPong: this.lastPongTime ? ((Date.now() - this.lastPongTime) / 1000).toFixed(1) + 's ago' : 'never'
154
+ };
155
+
156
+ console.log('Disconnected from server:', disconnectInfo);
157
+
147
158
  this.isConnected = false;
148
159
  this.isConnecting = false;
149
160
  this.disconnectTime = Date.now();
150
161
 
151
- // Calculate uptime
152
- if (this.lastConnectTime) {
153
- const uptime = (Date.now() - this.lastConnectTime) / 1000;
154
- console.log(`Connection uptime was ${uptime.toFixed(1)}s`);
155
- }
156
-
157
162
  this.notifyConnectionStatus(`Disconnected: ${reason}`, 'disconnected');
158
163
 
159
164
  // Emit disconnect callback
@@ -161,8 +166,21 @@ class SocketClient {
161
166
  callback(reason)
162
167
  );
163
168
 
164
- // Start auto-reconnect if it was an unexpected disconnect
165
- if (reason === 'transport close' || reason === 'ping timeout') {
169
+ // Detailed reason analysis for auto-reconnect decision
170
+ const reconnectReasons = [
171
+ 'transport close', // Network issue
172
+ 'ping timeout', // Server not responding
173
+ 'transport error', // Connection error
174
+ 'io server disconnect', // Server initiated disconnect (might be restart)
175
+ ];
176
+
177
+ if (reconnectReasons.includes(reason)) {
178
+ console.log(`Auto-reconnect triggered for reason: ${reason}`);
179
+ this.scheduleReconnect();
180
+ } else if (reason === 'io client disconnect') {
181
+ console.log('Client-initiated disconnect, not auto-reconnecting');
182
+ } else {
183
+ console.log(`Unknown disconnect reason: ${reason}, attempting reconnect anyway`);
166
184
  this.scheduleReconnect();
167
185
  }
168
186
  });
@@ -222,6 +240,12 @@ class SocketClient {
222
240
  });
223
241
  });
224
242
 
243
+ // Track pong responses from server
244
+ this.socket.on('pong', (data) => {
245
+ this.lastPongTime = Date.now();
246
+ // console.log('Received pong from server');
247
+ });
248
+
225
249
  // Session and event handlers (legacy/fallback)
226
250
  this.socket.on('session.started', (data) => {
227
251
  this.addEvent({ type: 'session', subtype: 'started', timestamp: new Date().toISOString(), data });
@@ -170,11 +170,28 @@ class ConnectionManagerService:
170
170
  # Publish to EventBus with topic format: hook.{event}
171
171
  topic = f"hook.{event}"
172
172
  self.event_bus.publish(topic, claude_event_data)
173
+
174
+ # Enhanced verification logging
173
175
  if DEBUG:
174
176
  print(f"✅ Published to EventBus: {topic}", file=sys.stderr)
177
+ # Get EventBus stats to verify publication
178
+ if hasattr(self.event_bus, "get_stats"):
179
+ stats = self.event_bus.get_stats()
180
+ print(
181
+ f"📊 EventBus stats after publish: {stats}", file=sys.stderr
182
+ )
183
+ # Log the number of data keys being published
184
+ if isinstance(claude_event_data, dict):
185
+ print(
186
+ f"📦 Published data keys: {list(claude_event_data.keys())}",
187
+ file=sys.stderr,
188
+ )
175
189
  except Exception as e:
176
190
  if DEBUG:
177
191
  print(f"⚠️ Failed to publish to EventBus: {e}", file=sys.stderr)
192
+ import traceback
193
+
194
+ traceback.print_exc(file=sys.stderr)
178
195
 
179
196
  # Warn if neither method is available
180
197
  if not self.connection_pool and not self.event_bus and DEBUG:
@@ -59,9 +59,30 @@ class DirectSocketIORelay:
59
59
 
60
60
  # Add debug logging for verification
61
61
  logger.info("[DirectRelay] Subscribed to hook.* events on EventBus")
62
+
63
+ # Check and log broadcaster availability
64
+ broadcaster_available = (
65
+ self.server
66
+ and hasattr(self.server, "broadcaster")
67
+ and self.server.broadcaster is not None
68
+ )
62
69
  logger.info(
63
- f"[DirectRelay] Server broadcaster available: {self.server and self.server.broadcaster is not None}"
70
+ f"[DirectRelay] Server broadcaster available: {broadcaster_available}"
64
71
  )
72
+ if not broadcaster_available:
73
+ if not self.server:
74
+ logger.warning(
75
+ "[DirectRelay] No server instance provided - events will not be relayed!"
76
+ )
77
+ elif not hasattr(self.server, "broadcaster"):
78
+ logger.warning(
79
+ "[DirectRelay] Server has no broadcaster attribute - events will not be relayed!"
80
+ )
81
+ else:
82
+ logger.warning(
83
+ "[DirectRelay] Server broadcaster is None - events will not be relayed!"
84
+ )
85
+
65
86
  logger.info(f"[DirectRelay] EventBus instance: {self.event_bus is not None}")
66
87
 
67
88
  # Mark as connected after successful subscription
@@ -76,9 +97,28 @@ class DirectSocketIORelay:
76
97
  data: The event data
77
98
  """
78
99
  try:
79
- # Log the event reception
100
+ # Enhanced debug logging for troubleshooting
80
101
  if self.debug:
81
102
  logger.debug(f"[DirectRelay] Received event: {event_type}")
103
+ logger.debug(f"[DirectRelay] Event data type: {type(data).__name__}")
104
+ logger.debug(
105
+ f"[DirectRelay] Event data keys: {list(data.keys()) if isinstance(data, dict) else 'not-dict'}"
106
+ )
107
+ logger.debug(
108
+ f"[DirectRelay] Relay state - enabled: {self.enabled}, connected: {self.connected}"
109
+ )
110
+ logger.debug(
111
+ f"[DirectRelay] Server state - has_server: {self.server is not None}, has_broadcaster: {self.server and hasattr(self.server, 'broadcaster') and self.server.broadcaster is not None}"
112
+ )
113
+
114
+ # Always log reception of important events
115
+ if event_type in [
116
+ "hook.pre_tool",
117
+ "hook.post_tool",
118
+ "hook.user_prompt",
119
+ "hook.subagent_stop",
120
+ ]:
121
+ logger.info(f"[DirectRelay] Processing important event: {event_type}")
82
122
 
83
123
  # Only relay hook events
84
124
  if event_type.startswith("hook."):
@@ -143,9 +183,26 @@ class DirectSocketIORelay:
143
183
  f"[DirectRelay] Broadcasted hook event: {event_type}"
144
184
  )
145
185
  else:
186
+ # Enhanced logging when broadcaster is not available
146
187
  logger.warning(
147
188
  f"[DirectRelay] Server broadcaster not available for {event_type}"
148
189
  )
190
+ if self.server:
191
+ logger.warning(
192
+ f"[DirectRelay] Server exists but broadcaster is None"
193
+ )
194
+ logger.warning(
195
+ f"[DirectRelay] Server type: {type(self.server).__name__}"
196
+ )
197
+ logger.warning(
198
+ f"[DirectRelay] Server has broadcaster attr: {hasattr(self.server, 'broadcaster')}"
199
+ )
200
+ if hasattr(self.server, "broadcaster"):
201
+ logger.warning(
202
+ f"[DirectRelay] Broadcaster value: {self.server.broadcaster}"
203
+ )
204
+ else:
205
+ logger.warning(f"[DirectRelay] Server is None")
149
206
  self.stats["events_failed"] += 1
150
207
 
151
208
  except Exception as e:
@@ -177,7 +177,7 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
177
177
  async def ping(sid):
178
178
  """Handle ping from client for health monitoring."""
179
179
  try:
180
- # Update activity in connection manager
180
+ # Update activity in connection manager - CRITICAL for preventing stale connections
181
181
  if self.server.connection_manager:
182
182
  await self.server.connection_manager.update_activity(sid, "ping")
183
183
 
@@ -293,23 +293,8 @@ class EnhancedConnectionEventHandler(BaseEventHandler):
293
293
  except Exception as e:
294
294
  self.logger.error(f"Error getting connection stats for {sid}: {e}")
295
295
 
296
- @sio.event
297
- async def heartbeat(sid):
298
- """Handle client heartbeat for connection monitoring."""
299
- try:
300
- # Update activity
301
- if self.server.connection_manager:
302
- await self.server.connection_manager.update_activity(sid, "event")
303
-
304
- # Send heartbeat response
305
- await sio.emit(
306
- "heartbeat_response",
307
- {"timestamp": datetime.now().isoformat(), "status": "alive"},
308
- room=sid,
309
- )
310
-
311
- except Exception as e:
312
- self.logger.error(f"Error handling heartbeat from {sid}: {e}")
296
+ # Heartbeat handler removed - Using Socket.IO's built-in ping/pong instead
297
+ # This prevents conflicting heartbeat systems that can cause disconnections
313
298
 
314
299
  self.logger.info("Enhanced connection event handlers registered")
315
300
 
@@ -70,22 +70,30 @@ class ClientConnection:
70
70
  metrics: ConnectionMetrics = field(default_factory=ConnectionMetrics)
71
71
  metadata: Dict[str, Any] = field(default_factory=dict)
72
72
 
73
- def is_healthy(self, timeout: float = 90.0) -> bool:
74
- """Check if connection is healthy based on activity."""
73
+ def is_healthy(self, timeout: float = 180.0) -> bool:
74
+ """Check if connection is healthy based on activity.
75
+
76
+ Args:
77
+ timeout: Seconds before considering connection unhealthy (default 180s)
78
+ """
75
79
  if self.state != ConnectionState.CONNECTED:
76
80
  return False
77
81
 
78
82
  now = time.time()
79
83
 
80
84
  # Check last activity (ping, pong, or event)
85
+ # Include metrics.last_activity for more comprehensive tracking
81
86
  last_activity = max(
82
87
  self.last_ping or 0,
83
88
  self.last_pong or 0,
84
89
  self.last_event or 0,
90
+ self.metrics.last_activity or 0,
85
91
  self.connected_at,
86
92
  )
87
93
 
88
- return (now - last_activity) < timeout
94
+ # Add grace period for network hiccups (additional 10% of timeout)
95
+ grace_period = timeout * 1.1
96
+ return (now - last_activity) < grace_period
89
97
 
90
98
  def calculate_quality(self) -> float:
91
99
  """Calculate connection quality score (0-1)."""
@@ -140,22 +148,26 @@ class ConnectionManager:
140
148
  - Automatic event replay on reconnection
141
149
  """
142
150
 
143
- def __init__(self, max_buffer_size: int = 1000, event_ttl: int = 300):
151
+ def __init__(self, max_buffer_size: int = None, event_ttl: int = None):
144
152
  """
145
- Initialize connection manager.
153
+ Initialize connection manager with centralized configuration.
146
154
 
147
155
  Args:
148
- max_buffer_size: Maximum events to buffer per client
149
- event_ttl: Time-to-live for buffered events in seconds
156
+ max_buffer_size: Maximum events to buffer per client (uses config if None)
157
+ event_ttl: Time-to-live for buffered events in seconds (uses config if None)
150
158
  """
159
+ from ....config.socketio_config import CONNECTION_CONFIG
160
+
151
161
  self.logger = get_logger(__name__)
152
162
  self.connections: Dict[str, ClientConnection] = {}
153
163
  self.client_mapping: Dict[str, str] = {} # client_id -> current sid
154
- self.max_buffer_size = max_buffer_size
155
- self.event_ttl = event_ttl
164
+
165
+ # Use centralized configuration with optional overrides
166
+ self.max_buffer_size = max_buffer_size or CONNECTION_CONFIG['max_events_buffer']
167
+ self.event_ttl = event_ttl or CONNECTION_CONFIG['event_ttl']
156
168
  self.global_sequence = 0
157
- self.health_check_interval = 30 # seconds
158
- self.stale_timeout = 90 # seconds
169
+ self.health_check_interval = CONNECTION_CONFIG['health_check_interval'] # 30 seconds
170
+ self.stale_timeout = CONNECTION_CONFIG['stale_timeout'] # 180 seconds (was 90)
159
171
  self.health_task = None
160
172
  self._lock = asyncio.Lock()
161
173
 
@@ -439,20 +451,39 @@ class ConnectionManager:
439
451
  if conn.is_healthy(self.stale_timeout):
440
452
  report["healthy"] += 1
441
453
  else:
442
- # Mark as stale
443
- conn.state = ConnectionState.STALE
444
- report["stale"] += 1
445
- self.logger.warning(
446
- f"Connection {conn.client_id} marked as stale "
447
- f"(last activity: {now - conn.metrics.last_activity:.1f}s ago)"
454
+ # Mark as stale only if really stale (no grace period activity)
455
+ last_activity = max(
456
+ conn.last_ping or 0,
457
+ conn.last_pong or 0,
458
+ conn.last_event or 0,
459
+ conn.metrics.last_activity or 0,
460
+ conn.connected_at,
448
461
  )
462
+ time_since_activity = now - last_activity
463
+
464
+ # Only mark as stale if significantly over timeout (2x)
465
+ if time_since_activity > (self.stale_timeout * 2):
466
+ conn.state = ConnectionState.STALE
467
+ report["stale"] += 1
468
+ self.logger.warning(
469
+ f"Connection {conn.client_id} marked as stale "
470
+ f"(last activity: {time_since_activity:.1f}s ago)"
471
+ )
472
+ else:
473
+ # Connection is borderline - keep it alive but log
474
+ report["healthy"] += 1
475
+ self.logger.debug(
476
+ f"Connection {conn.client_id} borderline "
477
+ f"(last activity: {time_since_activity:.1f}s ago)"
478
+ )
479
+
449
480
  elif conn.state == ConnectionState.DISCONNECTED:
450
481
  report["disconnected"] += 1
451
482
 
452
- # Clean up old disconnected connections
483
+ # Clean up old disconnected connections (be conservative)
453
484
  if (
454
485
  conn.disconnected_at
455
- and (now - conn.disconnected_at) > self.event_ttl
486
+ and (now - conn.disconnected_at) > (self.event_ttl * 2) # Double the TTL
456
487
  ):
457
488
  to_clean.append(sid)
458
489
 
@@ -158,14 +158,18 @@ class SocketIOServerCore:
158
158
  async def _start_server(self):
159
159
  """Start the Socket.IO server with aiohttp."""
160
160
  try:
161
- # Create Socket.IO server with proper ping/pong configuration
161
+ # Import centralized configuration for consistency
162
+ from ....config.socketio_config import CONNECTION_CONFIG
163
+
164
+ # Create Socket.IO server with centralized configuration
165
+ # CRITICAL: These values MUST match client settings to prevent disconnections
162
166
  self.sio = socketio.AsyncServer(
163
167
  cors_allowed_origins="*",
164
168
  logger=False, # Disable Socket.IO's own logging
165
169
  engineio_logger=False,
166
- ping_interval=25, # Send ping every 25 seconds
167
- ping_timeout=60, # Wait 60 seconds for pong response
168
- max_http_buffer_size=1e8, # 100MB max buffer
170
+ ping_interval=CONNECTION_CONFIG['ping_interval'], # 45 seconds from config
171
+ ping_timeout=CONNECTION_CONFIG['ping_timeout'], # 20 seconds from config
172
+ max_http_buffer_size=CONNECTION_CONFIG['max_http_buffer_size'], # 100MB from config
169
173
  )
170
174
 
171
175
  # Create aiohttp application
@@ -196,9 +200,13 @@ class SocketIOServerCore:
196
200
  if self.static_path:
197
201
  self.logger.info(f"Serving static files from: {self.static_path}")
198
202
 
199
- # Start heartbeat task
200
- self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
201
- self.logger.info("Started system heartbeat task")
203
+ # Conditionally start heartbeat task based on configuration
204
+ from ....config.socketio_config import CONNECTION_CONFIG
205
+ if CONNECTION_CONFIG.get('enable_extra_heartbeat', False):
206
+ self.heartbeat_task = asyncio.create_task(self._heartbeat_loop())
207
+ self.logger.info("Started system heartbeat task")
208
+ else:
209
+ self.logger.info("System heartbeat disabled (using Socket.IO ping/pong instead)")
202
210
 
203
211
  # Keep the server running
204
212
  while self.running:
@@ -173,35 +173,64 @@ class SocketIOServer(SocketIOServiceInterface):
173
173
  f"[{datetime.now().isoformat()}] Setting up EventBus integration...",
174
174
  flush=True,
175
175
  )
176
- try:
177
- self.eventbus_integration = EventBusIntegration(self)
176
+
177
+ # CRITICAL: Ensure broadcaster is fully initialized before setting up EventBus
178
+ # The relay needs the broadcaster to be ready to relay events
179
+ if not self.broadcaster:
180
+ self.logger.error(
181
+ "Broadcaster not initialized - cannot setup EventBus integration"
182
+ )
178
183
  print(
179
- f"[{datetime.now().isoformat()}] EventBusIntegration instance created",
184
+ f"[{datetime.now().isoformat()}] ERROR: Broadcaster not initialized",
185
+ flush=True,
186
+ )
187
+ else:
188
+ print(
189
+ f"[{datetime.now().isoformat()}] Broadcaster ready, proceeding with EventBus setup",
180
190
  flush=True,
181
191
  )
182
192
 
183
- if self.eventbus_integration.setup(self.port):
184
- self.logger.info("EventBus integration setup successful")
193
+ try:
194
+ self.eventbus_integration = EventBusIntegration(self)
185
195
  print(
186
- f"[{datetime.now().isoformat()}] EventBus integration setup successful",
196
+ f"[{datetime.now().isoformat()}] EventBusIntegration instance created",
187
197
  flush=True,
188
198
  )
189
- else:
190
- self.logger.warning("EventBus integration setup failed or disabled")
199
+
200
+ if self.eventbus_integration.setup(self.port):
201
+ self.logger.info("EventBus integration setup successful")
202
+ print(
203
+ f"[{datetime.now().isoformat()}] EventBus integration setup successful",
204
+ flush=True,
205
+ )
206
+
207
+ # Verify relay is connected and has broadcaster
208
+ if (
209
+ hasattr(self.eventbus_integration, "relay")
210
+ and self.eventbus_integration.relay
211
+ ):
212
+ relay_stats = self.eventbus_integration.relay.get_stats()
213
+ self.logger.info(f"EventBus relay stats: {relay_stats}")
214
+ print(
215
+ f"[{datetime.now().isoformat()}] EventBus relay stats: {relay_stats}",
216
+ flush=True,
217
+ )
218
+ else:
219
+ self.logger.warning("EventBus integration setup failed or disabled")
220
+ print(
221
+ f"[{datetime.now().isoformat()}] EventBus integration setup failed or disabled",
222
+ flush=True,
223
+ )
224
+ except Exception as e:
225
+ self.logger.error(f"Failed to setup EventBus integration: {e}")
191
226
  print(
192
- f"[{datetime.now().isoformat()}] EventBus integration setup failed or disabled",
227
+ f"[{datetime.now().isoformat()}] Failed to setup EventBus integration: {e}",
193
228
  flush=True,
194
229
  )
195
- except Exception as e:
196
- self.logger.error(f"Failed to setup EventBus integration: {e}")
197
- print(
198
- f"[{datetime.now().isoformat()}] Failed to setup EventBus integration: {e}",
199
- flush=True,
200
- )
201
- import traceback
230
+ import traceback
202
231
 
203
- traceback.print_exc()
204
- self.eventbus_integration = None
232
+ traceback.print_exc()
233
+ self.eventbus_integration = None
205
234
 
206
235
  # Update running state
207
236
  self.running = self.core.running
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-mpm
3
- Version: 4.1.6
3
+ Version: 4.1.7
4
4
  Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
5
5
  Author-email: Bob Matsuoka <bob@matsuoka.com>
6
6
  Maintainer: Claude MPM Team
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=toytnNjkIKPgQaGwDqQdC1rpNTAdSEc6Vja50d7Ovug,4
2
- claude_mpm/VERSION,sha256=w9Yv379QUvcGT8ToH1cuQa1SSo52GJWg_Ikc6-hN2jo,6
2
+ claude_mpm/VERSION,sha256=IL2PXQTVrQbwaRBS2I7XrtbIpLEqZiqKK8JTfEKKnxo,6
3
3
  claude_mpm/__init__.py,sha256=lyTZAYGH4DTaFGLRNWJKk5Q5oTjzN5I6AXmfVX-Jff0,1512
4
4
  claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
5
5
  claude_mpm/constants.py,sha256=5-k9ZbfDRe3WpGfHx3R0iTXokuOaZY-NcWcx1xkLTws,5678
@@ -22,8 +22,8 @@ claude_mpm/agents/frontmatter_validator.py,sha256=GiCxr13b-SFwvZNbNHDO0lFH9dFhsQ
22
22
  claude_mpm/agents/system_agent_config.py,sha256=HBDrtRld1ruPqrThA0j4vtMr4cBt0fT7qjDeCR79bps,24105
23
23
  claude_mpm/agents/schema/agent_schema.json,sha256=3p-_vcEAz5Py0ik7LPSJB9CQyE9L_zHhC82J0eCfn78,8784
24
24
  claude_mpm/agents/templates/__init__.py,sha256=kghxAWs3KvcAA9Esk3NI7caumYgW6fiW8vRO1-MEndU,2735
25
- claude_mpm/agents/templates/agent-manager.json,sha256=pRMILoAMxVZcc5g_XwStpJoz2K8LJo8sUyJB6bep6rI,628
26
- claude_mpm/agents/templates/agent-manager.md,sha256=vsy49vWxIlMVLX6H7_BtjPDaR4j_gkechvgWbN88aPI,8218
25
+ claude_mpm/agents/templates/agent-manager.json,sha256=4iBwEgIZLk54BAC5qzqBXUNmB6zB1fm-sqQJkMDAx-0,628
26
+ claude_mpm/agents/templates/agent-manager.md,sha256=evnX5dgSxxFmzOLV6K4aM4tgDFu7oPeNneMC_UqQQSs,11592
27
27
  claude_mpm/agents/templates/api_qa.json,sha256=hTdeNOgA2tzADCb2V9ExguFSvAv267yQYqa26AnOQHk,5786
28
28
  claude_mpm/agents/templates/code_analyzer.json,sha256=UOTXl6VTmzx1OoF5M4Bc9lQGriLf6Jq84rz30J77tEM,4718
29
29
  claude_mpm/agents/templates/data_engineer.json,sha256=rP3ffuaHwe0xGVpXShhVHTn1jEVMV2aAlnNGZL8j3jc,5629
@@ -95,7 +95,7 @@ claude_mpm/config/__init__.py,sha256=V2dyJQ8_gVCpNiCg8zYTQqE1RSeON5Zm8n5Ndkqhp1g
95
95
  claude_mpm/config/agent_config.py,sha256=d_cHE4HMkeZZrGjDGpBP655llV64p_E8DRDPPdsO1jg,14829
96
96
  claude_mpm/config/experimental_features.py,sha256=ma-Va7CVqOYrUEGZW6A4gdMRLHOpovHf8pHgP_3-xr0,7481
97
97
  claude_mpm/config/paths.py,sha256=Dl35_QF0kfC2I6Fy7ETcNjz7jlumNGt45qlRKqsPJrk,7611
98
- claude_mpm/config/socketio_config.py,sha256=qNLvjKZWUZEsNmSXUe0ueXu1q7-EOlxK3mdQquEZwTI,9714
98
+ claude_mpm/config/socketio_config.py,sha256=5R1zouAmG1Nz9UayHqx_OPEXvCwgI3OzIMOaFr2sgSo,11204
99
99
  claude_mpm/core/__init__.py,sha256=QhEX_NpiCprUp87bV75FnMapblMeqC5soNVJXVvZObc,887
100
100
  claude_mpm/core/agent_name_normalizer.py,sha256=_h92sTPltmvqevCjdKQ7EsOz-CAm1-hd8GOubWjuRgw,8907
101
101
  claude_mpm/core/agent_registry.py,sha256=cPQsOSIyrspr3KpS-P9MQ6Dv_BLXa9BWhs9RM0iu4C4,18798
@@ -178,7 +178,7 @@ claude_mpm/dashboard/static/dist/components/working-directory.js,sha256=7V1pBeux
178
178
  claude_mpm/dashboard/static/js/connection-manager.js,sha256=IJrtihUCQqkECXhiqy53vAJIdxNp7pJVIsV2ocos3As,17981
179
179
  claude_mpm/dashboard/static/js/dashboard.js,sha256=bacYhLFaxQIVOeRaUZProQ0Px6CeZFZmgnwZoHjKWls,71535
180
180
  claude_mpm/dashboard/static/js/extension-error-handler.js,sha256=DZHrJ3gbfv4nsjmZpNMj-Sc3GKjVJ5ds8lgoaLRnq5I,6274
181
- claude_mpm/dashboard/static/js/socket-client.js,sha256=i19ZYpUqO38kXubk5NpuVXlSlz8UhorTEYYCJFxbcLM,35984
181
+ claude_mpm/dashboard/static/js/socket-client.js,sha256=eXVTR3EQ6uK9PEjjmwT_8EpPDPPpCRbWiNBK5fS5CJk,37436
182
182
  claude_mpm/dashboard/static/js/components/agent-hierarchy.js,sha256=Xihxog_vJrk8VBEkDogV_wbye2GIFWmH71VQ1lETOHk,28243
183
183
  claude_mpm/dashboard/static/js/components/agent-inference.js,sha256=RUVZ_fLOyDkHYjrROen_Pzzay79Bh29eXp_GRIPbIRg,37493
184
184
  claude_mpm/dashboard/static/js/components/build-tracker.js,sha256=IBfKpoSKWD5QMPN4tOZl-E84Q3QADYVsZ76ickrKW8E,11485
@@ -216,7 +216,7 @@ claude_mpm/hooks/claude_hooks/memory_integration.py,sha256=TXLavDM9eEoakPX_n2Wtn
216
216
  claude_mpm/hooks/claude_hooks/response_tracking.py,sha256=4RcM54LyYeINf3A_toaSOPuD3oXS6jZtiRstHxgSOao,14990
217
217
  claude_mpm/hooks/claude_hooks/tool_analysis.py,sha256=3_o2PP9D7wEMwLriCtIBOw0cj2fSZfepN7lI4P1meSQ,7862
218
218
  claude_mpm/hooks/claude_hooks/services/__init__.py,sha256=OIYOKsUNw1BHYawOCp-KFK5kmQKuj92cCqCEPO0nwo0,585
219
- claude_mpm/hooks/claude_hooks/services/connection_manager.py,sha256=brkw3wE5m5RWa3fOo4noRO0xl0GpLWUPVacxcP6QYpM,7496
219
+ claude_mpm/hooks/claude_hooks/services/connection_manager.py,sha256=brj_FrSRwusbzyQo8a8DRGruTRqucfNWZNSDiNkbLe8,8290
220
220
  claude_mpm/hooks/claude_hooks/services/connection_manager_http.py,sha256=-DZ2seh1tpdo_RQPo5qkkJBcbjEkSpIFWOMMij1V0So,7686
221
221
  claude_mpm/hooks/claude_hooks/services/duplicate_detector.py,sha256=Fh9LmEMsVmQM9t0U1v2l_fuBwvNpVkl_0EF8Wu5KLHQ,3882
222
222
  claude_mpm/hooks/claude_hooks/services/state_manager.py,sha256=c7OldfemaMU37YGJxgO2uEcTOuPc-C80SRhzEH07j7I,11072
@@ -391,7 +391,7 @@ claude_mpm/services/diagnostics/checks/monitor_check.py,sha256=NUx5G1yjHWlukZmwh
391
391
  claude_mpm/services/diagnostics/checks/startup_log_check.py,sha256=DrXdml2rHvmhFBdb_sntE3xmwaP_DZIKjdVbCn8Dy7E,12258
392
392
  claude_mpm/services/event_bus/__init__.py,sha256=ETCo4a6puIeyVWAv55uCDjjhzNyUwbVAHEcAVkVapx8,688
393
393
  claude_mpm/services/event_bus/config.py,sha256=MJdOBK3XgLpW66N81tetThslnKsFIWYtbqRamyTDxlU,4943
394
- claude_mpm/services/event_bus/direct_relay.py,sha256=F_N5oqE49u1pVM22Vx2V3rQrGKQmqxEyHt-UF7RJKAk,7253
394
+ claude_mpm/services/event_bus/direct_relay.py,sha256=jijHA13xnKDFVIvefLFlpX2KsnSXwdU1X5QfUXsWwIc,9951
395
395
  claude_mpm/services/event_bus/event_bus.py,sha256=KQ8FpbHGsEVlygZj6CTYgugXL7Ht6KGlf1FAT4WJKEI,11920
396
396
  claude_mpm/services/event_bus/relay.py,sha256=yQ6Ow2rOj3dtNtYi2Vh4ToTouo-1eOiigtjnBM2w2Wo,9352
397
397
  claude_mpm/services/events/__init__.py,sha256=dna3DFYCxt9Y1jgK-0f2KPlX-jFo4n2dJyy9WKZkNlA,1092
@@ -495,7 +495,7 @@ claude_mpm/services/socketio/migration_utils.py,sha256=1pK_zGZ8Pd57pCg1O-3gKT8i7
495
495
  claude_mpm/services/socketio/handlers/__init__.py,sha256=zc1eo5hR5xDtnOZCeX38JNo8j9Vs10kV1cxZF20ZS4A,789
496
496
  claude_mpm/services/socketio/handlers/base.py,sha256=DjUODCOLTQSsC_-NP9yr-8g77arawmsRrM5KC06aDmw,4845
497
497
  claude_mpm/services/socketio/handlers/connection.py,sha256=sa6Rg5Y9tcf-e4PEb6f4fNhFQcpZ9WbwdiGXmkzlFTs,26781
498
- claude_mpm/services/socketio/handlers/connection_handler.py,sha256=7TMfi41nn_-5UUu_Zfh2u3sskefQ6adljgd_RDhqFLE,13749
498
+ claude_mpm/services/socketio/handlers/connection_handler.py,sha256=DTY0pa--HQu4C6-WCgE1zskvHm3gVuEKWwaSFNBAoec,13287
499
499
  claude_mpm/services/socketio/handlers/file.py,sha256=itpPa5OAow5_OXrTOXk0vsyuEYm4iVmxwN9xowy7jiY,8341
500
500
  claude_mpm/services/socketio/handlers/git.py,sha256=xIjGGShyVNXzCtJfydbbnFTJhqzXwHLzGhLrqgy0JGQ,37934
501
501
  claude_mpm/services/socketio/handlers/hook.py,sha256=JVQufL_u85z3r-n3fnKExuziheJ2lzY6d0GDoUB9iXs,7786
@@ -504,10 +504,10 @@ claude_mpm/services/socketio/handlers/project.py,sha256=yYqWlPfPanA7zH7dgQnP3EpO
504
504
  claude_mpm/services/socketio/handlers/registry.py,sha256=f4eELY04c23F0S8A5cxm9PDfSiGFumnfNgg7Ua0TsGU,7946
505
505
  claude_mpm/services/socketio/server/__init__.py,sha256=S486w-i-hBo3rNW_AtzxbasEgP32By-uI9zz7hzKz-o,640
506
506
  claude_mpm/services/socketio/server/broadcaster.py,sha256=y_D-fDhSD2NZI0cP9wuoB2nlI7VkKjzW_EjVmP-xd5Y,21375
507
- claude_mpm/services/socketio/server/connection_manager.py,sha256=P22O3FxLdADUxMDCSr0Vzr0CxnExgLYYh_Y13att6T4,17747
508
- claude_mpm/services/socketio/server/core.py,sha256=nY-EpBB-qsPgiSH-w46C3rtbBCNmXbG72zyYFSX65gI,21301
507
+ claude_mpm/services/socketio/server/connection_manager.py,sha256=sqOoORTFOXoUsW3_nrFfU4q-XYb4kEzP3TF6Dcrrh2o,19583
508
+ claude_mpm/services/socketio/server/core.py,sha256=QZZV9iB-0lWfVZ6u-Zj48MAW2xu9yCv2dMuYp4c2P3I,21921
509
509
  claude_mpm/services/socketio/server/eventbus_integration.py,sha256=6LkK8W9wTiNd8d9KoAH2IY1b4osyGGgGaJ0DmwIwnVM,7790
510
- claude_mpm/services/socketio/server/main.py,sha256=uKsh03fY-5_8wK9JOKm8DJz679Gv0mFkfoFxqrF4mxI,16466
510
+ claude_mpm/services/socketio/server/main.py,sha256=XyG5Bb3mtKpw5BNi8rRKaGB51Qmn_VtVU2km4ILFAVg,17820
511
511
  claude_mpm/services/ticket_services/__init__.py,sha256=I01W25n-tBWwZ0TD-dPA63nqzCU2KnpOvbqeysmaa2E,798
512
512
  claude_mpm/services/ticket_services/crud_service.py,sha256=4Ab1HONY219QSdRaLTgRInm6DNJXIUKiihyAree8y8A,11193
513
513
  claude_mpm/services/ticket_services/formatter_service.py,sha256=MQ981yaFuvXWUDLpHHiasc8BIFdeIeMS7Us0CgZHJ0A,9101
@@ -542,9 +542,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=zgiwLqh_17WxHpySvUPH65pb4bzIeUGOAYUJ
542
542
  claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
543
543
  claude_mpm/validation/agent_validator.py,sha256=3Lo6LK-Mw9IdnL_bd3zl_R6FkgSVDYKUUM7EeVVD3jc,20865
544
544
  claude_mpm/validation/frontmatter_validator.py,sha256=u8g4Eyd_9O6ugj7Un47oSGh3kqv4wMkuks2i_CtWRvM,7028
545
- claude_mpm-4.1.6.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
546
- claude_mpm-4.1.6.dist-info/METADATA,sha256=WzfyfF1uc4Ny1XukCgWXC1d884_DJAFtDzw0JHLTNu4,13386
547
- claude_mpm-4.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
548
- claude_mpm-4.1.6.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
549
- claude_mpm-4.1.6.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
550
- claude_mpm-4.1.6.dist-info/RECORD,,
545
+ claude_mpm-4.1.7.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
546
+ claude_mpm-4.1.7.dist-info/METADATA,sha256=NRjI5Nj-63NxX5dxg-9ahfdecMqorEGJgd8D6XODx2I,13386
547
+ claude_mpm-4.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
548
+ claude_mpm-4.1.7.dist-info/entry_points.txt,sha256=FDPZgz8JOvD-6iuXY2l9Zbo9zYVRuE4uz4Qr0vLeGOk,471
549
+ claude_mpm-4.1.7.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
550
+ claude_mpm-4.1.7.dist-info/RECORD,,