claude-mpm 4.0.32__py3-none-any.whl → 4.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +70 -2
  3. claude_mpm/agents/OUTPUT_STYLE.md +0 -11
  4. claude_mpm/agents/WORKFLOW.md +14 -2
  5. claude_mpm/agents/templates/documentation.json +51 -34
  6. claude_mpm/agents/templates/research.json +0 -11
  7. claude_mpm/cli/__init__.py +111 -33
  8. claude_mpm/cli/commands/agent_manager.py +10 -8
  9. claude_mpm/cli/commands/agents.py +82 -0
  10. claude_mpm/cli/commands/cleanup_orphaned_agents.py +150 -0
  11. claude_mpm/cli/commands/mcp_pipx_config.py +199 -0
  12. claude_mpm/cli/parsers/agents_parser.py +27 -0
  13. claude_mpm/cli/parsers/base_parser.py +6 -0
  14. claude_mpm/cli/startup_logging.py +75 -0
  15. claude_mpm/core/framework_loader.py +173 -84
  16. claude_mpm/dashboard/static/css/dashboard.css +449 -0
  17. claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
  18. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  19. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
  20. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  21. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  22. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  23. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  24. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +774 -0
  25. claude_mpm/dashboard/static/js/components/agent-inference.js +257 -3
  26. claude_mpm/dashboard/static/js/components/build-tracker.js +323 -0
  27. claude_mpm/dashboard/static/js/components/event-viewer.js +168 -39
  28. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +17 -0
  29. claude_mpm/dashboard/static/js/components/session-manager.js +23 -3
  30. claude_mpm/dashboard/static/js/components/socket-manager.js +2 -0
  31. claude_mpm/dashboard/static/js/dashboard.js +207 -31
  32. claude_mpm/dashboard/static/js/socket-client.js +92 -11
  33. claude_mpm/dashboard/templates/index.html +1 -0
  34. claude_mpm/hooks/claude_hooks/connection_pool.py +25 -4
  35. claude_mpm/hooks/claude_hooks/event_handlers.py +81 -19
  36. claude_mpm/hooks/claude_hooks/hook_handler.py +125 -163
  37. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +398 -0
  38. claude_mpm/hooks/claude_hooks/response_tracking.py +10 -0
  39. claude_mpm/services/agents/deployment/agent_deployment.py +34 -48
  40. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -1
  41. claude_mpm/services/agents/deployment/agent_template_builder.py +20 -11
  42. claude_mpm/services/agents/deployment/agent_version_manager.py +4 -1
  43. claude_mpm/services/agents/deployment/agents_directory_resolver.py +10 -25
  44. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +396 -13
  45. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +3 -2
  46. claude_mpm/services/agents/deployment/strategies/system_strategy.py +10 -3
  47. claude_mpm/services/agents/deployment/strategies/user_strategy.py +10 -14
  48. claude_mpm/services/agents/deployment/system_instructions_deployer.py +8 -85
  49. claude_mpm/services/agents/memory/content_manager.py +98 -105
  50. claude_mpm/services/event_bus/__init__.py +18 -0
  51. claude_mpm/services/event_bus/config.py +165 -0
  52. claude_mpm/services/event_bus/event_bus.py +349 -0
  53. claude_mpm/services/event_bus/relay.py +297 -0
  54. claude_mpm/services/events/__init__.py +44 -0
  55. claude_mpm/services/events/consumers/__init__.py +18 -0
  56. claude_mpm/services/events/consumers/dead_letter.py +296 -0
  57. claude_mpm/services/events/consumers/logging.py +183 -0
  58. claude_mpm/services/events/consumers/metrics.py +242 -0
  59. claude_mpm/services/events/consumers/socketio.py +376 -0
  60. claude_mpm/services/events/core.py +470 -0
  61. claude_mpm/services/events/interfaces.py +230 -0
  62. claude_mpm/services/events/producers/__init__.py +14 -0
  63. claude_mpm/services/events/producers/hook.py +269 -0
  64. claude_mpm/services/events/producers/system.py +327 -0
  65. claude_mpm/services/mcp_gateway/auto_configure.py +372 -0
  66. claude_mpm/services/mcp_gateway/core/process_pool.py +411 -0
  67. claude_mpm/services/mcp_gateway/server/stdio_server.py +13 -0
  68. claude_mpm/services/monitor_build_service.py +345 -0
  69. claude_mpm/services/socketio/event_normalizer.py +667 -0
  70. claude_mpm/services/socketio/handlers/connection.py +81 -23
  71. claude_mpm/services/socketio/handlers/hook.py +14 -5
  72. claude_mpm/services/socketio/migration_utils.py +329 -0
  73. claude_mpm/services/socketio/server/broadcaster.py +26 -33
  74. claude_mpm/services/socketio/server/core.py +29 -5
  75. claude_mpm/services/socketio/server/eventbus_integration.py +189 -0
  76. claude_mpm/services/socketio/server/main.py +25 -0
  77. {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/METADATA +28 -9
  78. {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/RECORD +82 -56
  79. {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/WHEEL +0 -0
  80. {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/entry_points.txt +0 -0
  81. {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/licenses/LICENSE +0 -0
  82. {claude_mpm-4.0.32.dist-info → claude_mpm-4.1.0.dist-info}/top_level.txt +0 -0
@@ -19,11 +19,13 @@ import { EventViewer } from '@components/event-viewer.js';
19
19
  import { ModuleViewer } from '@components/module-viewer.js';
20
20
  import { SessionManager } from '@components/session-manager.js';
21
21
  import { AgentInference } from '@components/agent-inference.js';
22
+ import { AgentHierarchy } from '@components/agent-hierarchy.js';
22
23
  import { UIStateManager } from '@components/ui-state-manager.js';
23
24
  import { EventProcessor } from '@components/event-processor.js';
24
25
  import { ExportManager } from '@components/export-manager.js';
25
26
  import { WorkingDirectoryManager } from '@components/working-directory.js';
26
27
  import { FileToolTracker } from '@components/file-tool-tracker.js';
28
+ import { BuildTracker } from '@components/build-tracker.js';
27
29
  class Dashboard {
28
30
  constructor() {
29
31
  // Core components (existing)
@@ -34,11 +36,13 @@ class Dashboard {
34
36
  // New modular components
35
37
  this.socketManager = null;
36
38
  this.agentInference = null;
39
+ this.agentHierarchy = null;
37
40
  this.uiStateManager = null;
38
41
  this.eventProcessor = null;
39
42
  this.exportManager = null;
40
43
  this.workingDirectoryManager = null;
41
44
  this.fileToolTracker = null;
45
+ this.buildTracker = null;
42
46
 
43
47
  // Initialize the dashboard
44
48
  this.init();
@@ -50,23 +54,75 @@ class Dashboard {
50
54
  init() {
51
55
  console.log('Initializing refactored Claude MPM Dashboard...');
52
56
 
53
- // Initialize modules in dependency order
54
- this.initializeSocketManager();
55
- this.initializeCoreComponents();
56
- this.initializeAgentInference();
57
- this.initializeUIStateManager();
58
- this.initializeWorkingDirectoryManager();
59
- this.initializeFileToolTracker();
60
- this.initializeEventProcessor();
61
- this.initializeExportManager();
62
-
63
- // Set up inter-module communication
64
- this.setupModuleInteractions();
65
-
66
- // Initialize from URL parameters
67
- this.initializeFromURL();
57
+ try {
58
+ // Initialize modules in dependency order
59
+ this.initializeSocketManager();
60
+ this.initializeCoreComponents();
61
+ this.initializeBuildTracker();
62
+ this.initializeAgentInference();
63
+ this.initializeAgentHierarchy();
64
+ this.initializeUIStateManager();
65
+ this.initializeWorkingDirectoryManager();
66
+ this.initializeFileToolTracker();
67
+ this.initializeEventProcessor();
68
+ this.initializeExportManager();
69
+
70
+ // Set up inter-module communication
71
+ this.setupModuleInteractions();
72
+
73
+ // Initialize from URL parameters
74
+ this.initializeFromURL();
75
+
76
+ console.log('Claude MPM Dashboard initialized successfully');
77
+ } catch (error) {
78
+ console.error('Error during dashboard initialization:', error);
79
+ // Re-throw to be caught by DOMContentLoaded handler
80
+ throw error;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Validate that all critical components are initialized
86
+ * WHY: Ensures dashboard is in a valid state after initialization
87
+ */
88
+ validateInitialization() {
89
+ const criticalComponents = [
90
+ { name: 'socketManager', component: this.socketManager },
91
+ { name: 'eventViewer', component: this.eventViewer },
92
+ { name: 'agentHierarchy', component: this.agentHierarchy }
93
+ ];
94
+
95
+ const missing = criticalComponents.filter(c => !c.component);
96
+ if (missing.length > 0) {
97
+ console.warn('Missing critical components:', missing.map(c => c.name));
98
+ } else {
99
+ console.log('All critical components initialized');
100
+ }
101
+ }
68
102
 
69
- console.log('Claude MPM Dashboard initialized successfully');
103
+ /**
104
+ * Post-initialization setup that requires window.dashboard to be set
105
+ * WHY: Some components need to reference window.dashboard but it's not available
106
+ * during constructor execution. This method is called after the Dashboard instance
107
+ * is assigned to window.dashboard, ensuring proper initialization order.
108
+ *
109
+ * DESIGN DECISION: Separate post-init phase prevents "cannot read property of undefined"
110
+ * errors when components try to access window.dashboard during construction.
111
+ */
112
+ postInit() {
113
+ try {
114
+ // Set global reference for agent hierarchy after dashboard is available
115
+ if (this.agentHierarchy) {
116
+ window.dashboard.agentHierarchy = this.agentHierarchy;
117
+ console.log('Agent hierarchy global reference set');
118
+ }
119
+
120
+ // Initialize any other components that need window.dashboard
121
+ this.validateInitialization();
122
+ } catch (error) {
123
+ console.error('Error in dashboard postInit:', error);
124
+ // Continue execution - non-critical error
125
+ }
70
126
  }
71
127
 
72
128
  /**
@@ -98,6 +154,28 @@ class Dashboard {
98
154
  window.sessionManager = this.sessionManager;
99
155
  }
100
156
 
157
+ /**
158
+ * Initialize build tracker
159
+ */
160
+ initializeBuildTracker() {
161
+ this.buildTracker = new BuildTracker();
162
+
163
+ // Set the socket client for receiving updates
164
+ this.buildTracker.setSocketClient(this.socketClient);
165
+
166
+ // Mount to header - find the best location
167
+ const headerTitle = document.querySelector('.header-title');
168
+ if (headerTitle) {
169
+ // Insert after the title and status badge
170
+ this.buildTracker.mount(headerTitle);
171
+ } else {
172
+ console.warn('Could not find header-title element for build tracker');
173
+ }
174
+
175
+ // Make available globally for debugging
176
+ window.buildTracker = this.buildTracker;
177
+ }
178
+
101
179
  /**
102
180
  * Initialize agent inference system
103
181
  */
@@ -105,6 +183,28 @@ class Dashboard {
105
183
  this.agentInference = new AgentInference(this.eventViewer);
106
184
  this.agentInference.initialize();
107
185
  }
186
+
187
+ /**
188
+ * Initialize agent hierarchy component
189
+ * WHY: Creates the agent hierarchy visualization component but defers global
190
+ * reference setting to postInit() to avoid initialization order issues.
191
+ */
192
+ initializeAgentHierarchy() {
193
+ try {
194
+ this.agentHierarchy = new AgentHierarchy(this.agentInference, this.eventViewer);
195
+ // Global reference will be set in postInit() after window.dashboard exists
196
+ console.log('Agent hierarchy component created');
197
+ } catch (error) {
198
+ console.error('Failed to initialize agent hierarchy:', error);
199
+ // Create a stub to prevent further errors
200
+ this.agentHierarchy = {
201
+ render: () => '<div class="error">Agent hierarchy unavailable</div>',
202
+ expandAllNodes: () => {},
203
+ collapseAllNodes: () => {},
204
+ updateWithNewEvents: () => {}
205
+ };
206
+ }
207
+ }
108
208
 
109
209
  /**
110
210
  * Initialize UI state manager
@@ -154,6 +254,9 @@ class Dashboard {
154
254
 
155
255
  // Process agent inference for new events
156
256
  this.agentInference.processAgentInference();
257
+
258
+ // Update agent hierarchy with new events
259
+ this.agentHierarchy.updateWithNewEvents(events);
157
260
 
158
261
  // Auto-scroll events list if on events tab
159
262
  if (this.uiStateManager.getCurrentTab() === 'events') {
@@ -291,26 +394,74 @@ class Dashboard {
291
394
  }
292
395
 
293
396
  /**
294
- * Render agents tab with unique instance view (one row per PM delegation)
397
+ * Render agents tab with hierarchical view
295
398
  */
296
399
  renderAgents() {
297
400
  const agentsList = document.getElementById('agents-list');
298
401
  if (!agentsList) return;
299
-
300
- // Process agent inference to get PM delegations
301
- this.agentInference.processAgentInference();
302
-
303
- // Generate HTML for unique agent instances
304
- const events = this.eventProcessor.getFilteredEventsForTab('agents');
305
- const agentHTML = this.eventProcessor.generateAgentHTML(events);
306
-
307
- agentsList.innerHTML = agentHTML;
308
- this.exportManager.scrollListToBottom('agents-list');
309
-
310
- // Update filter dropdowns with unique instances
402
+
403
+ // Get filter values
404
+ const searchText = document.getElementById('agents-search-input')?.value || '';
405
+ const agentType = document.getElementById('agents-type-filter')?.value || '';
406
+
407
+ // Build filters object
408
+ const filters = {};
409
+ if (searchText) filters.searchText = searchText;
410
+ if (agentType) filters.agentType = agentType;
411
+
412
+ // Generate hierarchical HTML
413
+ const hierarchyHTML = this.agentHierarchy.render(filters);
414
+ agentsList.innerHTML = hierarchyHTML;
415
+
416
+ // Add expand/collapse all buttons if not present
417
+ this.addHierarchyControls();
418
+
419
+ // Update filter dropdowns with available agent types
311
420
  const uniqueInstances = this.agentInference.getUniqueAgentInstances();
312
421
  this.updateAgentsFilterDropdowns(uniqueInstances);
313
422
  }
423
+
424
+ /**
425
+ * Add hierarchy control buttons
426
+ */
427
+ addHierarchyControls() {
428
+ const tabFilters = document.querySelector('#agents-tab .tab-filters');
429
+ if (!tabFilters) return;
430
+
431
+ // Check if controls already exist
432
+ if (document.getElementById('hierarchy-controls')) return;
433
+
434
+ // Create control buttons
435
+ const controls = document.createElement('div');
436
+ controls.id = 'hierarchy-controls';
437
+ controls.className = 'hierarchy-controls';
438
+ controls.innerHTML = `
439
+ <button data-action="expand-all"
440
+ class="hierarchy-btn hierarchy-expand-all">
441
+ Expand All
442
+ </button>
443
+ <button data-action="collapse-all"
444
+ class="hierarchy-btn hierarchy-collapse-all">
445
+ Collapse All
446
+ </button>
447
+ `;
448
+
449
+ // Add safe event listeners
450
+ controls.addEventListener('click', (event) => {
451
+ const action = event.target.dataset.action;
452
+ if (action && window.dashboard && window.dashboard.agentHierarchy) {
453
+ if (action === 'expand-all') {
454
+ window.dashboard.agentHierarchy.expandAllNodes();
455
+ window.dashboard.renderAgents();
456
+ } else if (action === 'collapse-all') {
457
+ window.dashboard.agentHierarchy.collapseAllNodes();
458
+ window.dashboard.renderAgents();
459
+ }
460
+ }
461
+ });
462
+
463
+ tabFilters.appendChild(controls);
464
+ }
314
465
 
315
466
  /**
316
467
  * Render tools tab with unique instance view (one row per unique tool call)
@@ -1810,8 +1961,33 @@ window.showAgentInstanceDetails = function(instanceId) {
1810
1961
 
1811
1962
  // Initialize dashboard when page loads
1812
1963
  document.addEventListener('DOMContentLoaded', function() {
1813
- window.dashboard = new Dashboard();
1814
- console.log('Dashboard loaded and initialized');
1964
+ try {
1965
+ // Create dashboard instance
1966
+ window.dashboard = new Dashboard();
1967
+
1968
+ // Call post-initialization setup that requires window.dashboard
1969
+ // This must happen after window.dashboard is set
1970
+ if (window.dashboard && typeof window.dashboard.postInit === 'function') {
1971
+ window.dashboard.postInit();
1972
+ }
1973
+
1974
+ console.log('Dashboard loaded and initialized successfully');
1975
+
1976
+ // Dispatch custom event to signal dashboard ready
1977
+ document.dispatchEvent(new CustomEvent('dashboardReady', {
1978
+ detail: { dashboard: window.dashboard }
1979
+ }));
1980
+ } catch (error) {
1981
+ console.error('Failed to initialize dashboard:', error);
1982
+ // Show user-friendly error message
1983
+ document.body.innerHTML = `
1984
+ <div style="padding: 20px; font-family: sans-serif;">
1985
+ <h1>Dashboard Initialization Error</h1>
1986
+ <p>The dashboard failed to load properly. Please refresh the page or check the console for details.</p>
1987
+ <pre style="background: #f5f5f5; padding: 10px; border-radius: 4px;">${error.message}</pre>
1988
+ </div>
1989
+ `;
1990
+ }
1815
1991
  });
1816
1992
 
1817
1993
  // ES6 Module export
@@ -16,6 +16,12 @@ class SocketClient {
16
16
  error: [],
17
17
  event: []
18
18
  };
19
+
20
+ // Event schema validation
21
+ this.eventSchema = {
22
+ required: ['source', 'type', 'subtype', 'timestamp', 'data'],
23
+ optional: ['event', 'session_id']
24
+ };
19
25
 
20
26
  // Connection state
21
27
  this.isConnected = false;
@@ -41,7 +47,7 @@ class SocketClient {
41
47
  // Health monitoring
42
48
  this.lastPingTime = null;
43
49
  this.lastPongTime = null;
44
- this.pingTimeout = 40000; // 40 seconds (server sends every 30s)
50
+ this.pingTimeout = 90000; // 90 seconds for health check (more lenient than Socket.IO timeout)
45
51
  this.healthCheckInterval = null;
46
52
 
47
53
  // Start periodic status check as fallback mechanism
@@ -91,11 +97,13 @@ class SocketClient {
91
97
  autoConnect: true,
92
98
  reconnection: true,
93
99
  reconnectionDelay: 1000,
94
- reconnectionDelayMax: 10000,
95
- maxReconnectionAttempts: 10,
96
- timeout: 10000,
100
+ reconnectionDelayMax: 5000,
101
+ reconnectionAttempts: Infinity, // Keep trying indefinitely
102
+ timeout: 20000, // Increase connection timeout
97
103
  forceNew: true,
98
- transports: ['websocket', 'polling']
104
+ transports: ['websocket', 'polling'],
105
+ pingInterval: 25000, // Match server setting
106
+ pingTimeout: 60000 // Match server setting
99
107
  });
100
108
 
101
109
  this.setupSocketHandlers();
@@ -187,11 +195,18 @@ class SocketClient {
187
195
 
188
196
  // Primary event handler - this is what the server actually emits
189
197
  this.socket.on('claude_event', (data) => {
190
- // console.log('Received claude_event:', data);
191
-
192
- // Transform event to match expected format
193
- const transformedEvent = this.transformEvent(data);
194
- // console.log('Transformed event:', transformedEvent);
198
+ console.log('Received claude_event:', data);
199
+
200
+ // Validate event schema
201
+ const validatedEvent = this.validateEventSchema(data);
202
+ if (!validatedEvent) {
203
+ console.warn('Invalid event schema received:', data);
204
+ return;
205
+ }
206
+
207
+ // Transform event to match expected format (for backward compatibility)
208
+ const transformedEvent = this.transformEvent(validatedEvent);
209
+ console.log('Transformed event:', transformedEvent);
195
210
  this.addEvent(transformedEvent);
196
211
  });
197
212
 
@@ -628,6 +643,51 @@ class SocketClient {
628
643
  };
629
644
  }
630
645
 
646
+ /**
647
+ * Validate event against expected schema
648
+ * @param {Object} eventData - Raw event data
649
+ * @returns {Object|null} Validated event or null if invalid
650
+ */
651
+ validateEventSchema(eventData) {
652
+ if (!eventData || typeof eventData !== 'object') {
653
+ console.warn('Event data is not an object:', eventData);
654
+ return null;
655
+ }
656
+
657
+ // Make a copy to avoid modifying the original
658
+ const validated = { ...eventData };
659
+
660
+ // Check and provide defaults for required fields
661
+ if (!validated.source) {
662
+ validated.source = 'system'; // Default source for backward compatibility
663
+ }
664
+ if (!validated.type) {
665
+ // If there's an event field, use it as the type
666
+ if (validated.event) {
667
+ validated.type = validated.event;
668
+ } else {
669
+ validated.type = 'unknown';
670
+ }
671
+ }
672
+ if (!validated.subtype) {
673
+ validated.subtype = 'generic';
674
+ }
675
+ if (!validated.timestamp) {
676
+ validated.timestamp = new Date().toISOString();
677
+ }
678
+ if (!validated.data) {
679
+ validated.data = {};
680
+ }
681
+
682
+ // Ensure data field is an object
683
+ if (validated.data && typeof validated.data !== 'object') {
684
+ validated.data = { value: validated.data };
685
+ }
686
+
687
+ console.log('Validated event:', validated);
688
+ return validated;
689
+ }
690
+
631
691
  /**
632
692
  * Transform received event to match expected dashboard format
633
693
  * @param {Object} eventData - Raw event data from server
@@ -718,7 +778,14 @@ class SocketClient {
718
778
  Object.keys(eventData.data).forEach(key => {
719
779
  // Only copy if not a protected field
720
780
  if (!protectedFields.includes(key)) {
721
- transformedEvent[key] = eventData.data[key];
781
+ // Special handling for tool_parameters to ensure it's properly preserved
782
+ // This is critical for file path extraction in file-tool-tracker
783
+ if (key === 'tool_parameters' && typeof eventData.data[key] === 'object') {
784
+ // Deep copy the tool_parameters object to preserve all nested fields
785
+ transformedEvent[key] = JSON.parse(JSON.stringify(eventData.data[key]));
786
+ } else {
787
+ transformedEvent[key] = eventData.data[key];
788
+ }
722
789
  } else {
723
790
  // Log warning if data field would overwrite a protected field
724
791
  console.warn(`Protected field '${key}' in data object was not copied to top level to preserve event structure`);
@@ -735,9 +802,23 @@ class SocketClient {
735
802
  type: transformedEvent.type,
736
803
  subtype: transformedEvent.subtype,
737
804
  tool_name: transformedEvent.tool_name,
805
+ has_tool_parameters: !!transformedEvent.tool_parameters,
806
+ tool_parameters: transformedEvent.tool_parameters,
738
807
  has_data: !!transformedEvent.data,
739
808
  keys: Object.keys(transformedEvent).filter(k => k !== 'data')
740
809
  });
810
+
811
+ // Extra debug logging for file-related tools
812
+ const fileTools = ['Read', 'Write', 'Edit', 'MultiEdit', 'NotebookEdit'];
813
+ if (fileTools.includes(transformedEvent.tool_name)) {
814
+ console.log('File tool event details:', {
815
+ tool_name: transformedEvent.tool_name,
816
+ file_path: transformedEvent.tool_parameters?.file_path,
817
+ path: transformedEvent.tool_parameters?.path,
818
+ notebook_path: transformedEvent.tool_parameters?.notebook_path,
819
+ full_parameters: transformedEvent.tool_parameters
820
+ });
821
+ }
741
822
  }
742
823
 
743
824
  return transformedEvent;
@@ -141,6 +141,7 @@
141
141
  <div id="connection-status" class="status-badge status-disconnected">
142
142
  <span>●</span> Disconnected
143
143
  </div>
144
+ <!-- Version display will be inserted here by BuildTracker component -->
144
145
  </div>
145
146
  <div class="metrics-widget">
146
147
  <div class="metric-mini">
@@ -1,7 +1,18 @@
1
1
  #!/usr/bin/env python3
2
- """Socket.IO connection pool for Claude Code hook handler.
2
+ """[DEPRECATED] Socket.IO connection pool for Claude Code hook handler.
3
3
 
4
- This module provides connection pooling for Socket.IO clients to reduce
4
+ DEPRECATION NOTICE: As of v4.0.35, this module is deprecated.
5
+ All event emission now goes through the EventBus, which handles
6
+ Socket.IO connections via its relay component. This provides:
7
+ - Single event path (no duplicates)
8
+ - Better separation of concerns
9
+ - Centralized connection management
10
+ - More resilient architecture
11
+
12
+ This module is kept for backward compatibility but will be removed in v5.0.0.
13
+ Please use EventBus.publish() instead of direct Socket.IO emission.
14
+
15
+ Original purpose: Provided connection pooling for Socket.IO clients to reduce
5
16
  connection overhead and implement circuit breaker patterns.
6
17
  """
7
18
 
@@ -109,7 +120,12 @@ class SocketIOConnectionPool:
109
120
  if client.connected:
110
121
  # Send a keep-alive ping to establish the connection
111
122
  try:
112
- client.emit('ping', {'timestamp': time.time()})
123
+ client.emit('ping', {
124
+ 'type': 'system',
125
+ 'subtype': 'ping',
126
+ 'timestamp': time.time(),
127
+ 'source': 'connection_pool'
128
+ })
113
129
  except:
114
130
  pass # Ignore ping errors
115
131
  return client
@@ -137,7 +153,12 @@ class SocketIOConnectionPool:
137
153
  # This helps detect zombie connections
138
154
  try:
139
155
  # Just emit a ping, don't wait for response (faster)
140
- client.emit('ping', {'timestamp': time.time()})
156
+ client.emit('ping', {
157
+ 'type': 'system',
158
+ 'subtype': 'ping',
159
+ 'timestamp': time.time(),
160
+ 'source': 'connection_pool'
161
+ })
141
162
  return True
142
163
  except:
143
164
  # If ping fails, connection might be dead