claude-mpm 3.4.10__py3-none-any.whl → 3.4.14__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 (29) hide show
  1. claude_mpm/cli/commands/run.py +10 -10
  2. claude_mpm/dashboard/index.html +13 -0
  3. claude_mpm/dashboard/static/css/dashboard.css +2722 -0
  4. claude_mpm/dashboard/static/js/components/agent-inference.js +619 -0
  5. claude_mpm/dashboard/static/js/components/event-processor.js +641 -0
  6. claude_mpm/dashboard/static/js/components/event-viewer.js +914 -0
  7. claude_mpm/dashboard/static/js/components/export-manager.js +362 -0
  8. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +611 -0
  9. claude_mpm/dashboard/static/js/components/hud-library-loader.js +211 -0
  10. claude_mpm/dashboard/static/js/components/hud-manager.js +671 -0
  11. claude_mpm/dashboard/static/js/components/hud-visualizer.js +1718 -0
  12. claude_mpm/dashboard/static/js/components/module-viewer.js +2701 -0
  13. claude_mpm/dashboard/static/js/components/session-manager.js +520 -0
  14. claude_mpm/dashboard/static/js/components/socket-manager.js +343 -0
  15. claude_mpm/dashboard/static/js/components/ui-state-manager.js +427 -0
  16. claude_mpm/dashboard/static/js/components/working-directory.js +866 -0
  17. claude_mpm/dashboard/static/js/dashboard-original.js +4134 -0
  18. claude_mpm/dashboard/static/js/dashboard.js +1978 -0
  19. claude_mpm/dashboard/static/js/socket-client.js +537 -0
  20. claude_mpm/dashboard/templates/index.html +346 -0
  21. claude_mpm/dashboard/test_dashboard.html +372 -0
  22. claude_mpm/scripts/socketio_daemon.py +51 -6
  23. claude_mpm/services/socketio_server.py +41 -5
  24. {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/METADATA +2 -1
  25. {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/RECORD +29 -9
  26. {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/WHEEL +0 -0
  27. {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/entry_points.txt +0 -0
  28. {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/licenses/LICENSE +0 -0
  29. {claude_mpm-3.4.10.dist-info → claude_mpm-3.4.14.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,520 @@
1
+ /**
2
+ * Session Manager Component
3
+ * Handles session selection and management
4
+ */
5
+
6
+ class SessionManager {
7
+ constructor(socketClient) {
8
+ this.socketClient = socketClient;
9
+ this.sessions = new Map();
10
+ this.currentSessionId = null;
11
+ this.selectedSessionId = '';
12
+
13
+ this.init();
14
+ }
15
+
16
+ /**
17
+ * Initialize the session manager
18
+ */
19
+ init() {
20
+ this.setupEventHandlers();
21
+ this.setupSocketListeners();
22
+ this.updateSessionSelect();
23
+ }
24
+
25
+ /**
26
+ * Setup event handlers for UI controls
27
+ */
28
+ setupEventHandlers() {
29
+ // Session selection dropdown
30
+ const sessionSelect = document.getElementById('session-select');
31
+ if (sessionSelect) {
32
+ sessionSelect.addEventListener('change', (e) => {
33
+ this.selectedSessionId = e.target.value;
34
+ this.onSessionFilterChanged();
35
+
36
+ // Load working directory for this session
37
+ if (window.dashboard && window.dashboard.loadWorkingDirectoryForSession) {
38
+ window.dashboard.loadWorkingDirectoryForSession(e.target.value);
39
+ }
40
+ });
41
+ }
42
+
43
+ // Refresh sessions button
44
+ const refreshBtn = document.querySelector('button[onclick="refreshSessions()"]');
45
+ if (refreshBtn) {
46
+ refreshBtn.addEventListener('click', () => {
47
+ this.refreshSessions();
48
+ });
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Setup socket event listeners
54
+ */
55
+ setupSocketListeners() {
56
+ // Listen for socket event updates
57
+ this.socketClient.onEventUpdate((events, sessions) => {
58
+ this.sessions = sessions;
59
+ this.updateSessionSelect();
60
+ // Update footer info when new events arrive
61
+ this.updateFooterInfo();
62
+ });
63
+
64
+ // Listen for connection status changes
65
+ document.addEventListener('socketConnectionStatus', (e) => {
66
+ if (e.detail.type === 'connected') {
67
+ // Request fresh session data when connected
68
+ setTimeout(() => this.refreshSessions(), 1000);
69
+ }
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Update the session selection dropdown
75
+ */
76
+ updateSessionSelect() {
77
+ const sessionSelect = document.getElementById('session-select');
78
+ if (!sessionSelect) return;
79
+
80
+ // Store current selection
81
+ const currentSelection = sessionSelect.value;
82
+
83
+ // Clear existing options except default ones
84
+ sessionSelect.innerHTML = `
85
+ <option value="">All Sessions</option>
86
+ `;
87
+
88
+ // Add sessions from the sessions map
89
+ if (this.sessions && this.sessions.size > 0) {
90
+ const sortedSessions = Array.from(this.sessions.values())
91
+ .sort((a, b) => new Date(b.lastActivity || b.startTime) - new Date(a.lastActivity || a.startTime));
92
+
93
+ sortedSessions.forEach(session => {
94
+ const option = document.createElement('option');
95
+ option.value = session.id;
96
+
97
+ // Format session display text
98
+ const startTime = new Date(session.startTime || session.last_activity).toLocaleString();
99
+ const eventCount = session.eventCount || session.event_count || 0;
100
+ const isActive = session.id === this.currentSessionId;
101
+
102
+ option.textContent = `${session.id.substring(0, 8)}... (${eventCount} events, ${startTime})${isActive ? ' [ACTIVE]' : ''}`;
103
+ sessionSelect.appendChild(option);
104
+ });
105
+ }
106
+
107
+ // Restore selection if it still exists
108
+ if (currentSelection && Array.from(sessionSelect.options).some(opt => opt.value === currentSelection)) {
109
+ sessionSelect.value = currentSelection;
110
+ this.selectedSessionId = currentSelection;
111
+ // Trigger events for restored selection
112
+ this.onSessionFilterChanged();
113
+ } else {
114
+ this.selectedSessionId = sessionSelect.value;
115
+ // Trigger events for new selection
116
+ if (this.selectedSessionId) {
117
+ this.onSessionFilterChanged();
118
+ }
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Handle session filter change
124
+ */
125
+ onSessionFilterChanged() {
126
+ // Notify event viewer about filter change
127
+ const eventViewer = window.eventViewer;
128
+ if (eventViewer) {
129
+ eventViewer.setSessionFilter(this.selectedSessionId);
130
+ }
131
+
132
+ // Update footer information
133
+ this.updateFooterInfo();
134
+
135
+ // Dispatch custom event for other components
136
+ document.dispatchEvent(new CustomEvent('sessionFilterChanged', {
137
+ detail: { sessionId: this.selectedSessionId }
138
+ }));
139
+
140
+ // Also dispatch sessionChanged for backward compatibility with other components
141
+ document.dispatchEvent(new CustomEvent('sessionChanged', {
142
+ detail: { sessionId: this.selectedSessionId }
143
+ }));
144
+ }
145
+
146
+ /**
147
+ * Refresh sessions from server
148
+ */
149
+ refreshSessions() {
150
+ if (this.socketClient && this.socketClient.getConnectionState().isConnected) {
151
+ console.log('Refreshing sessions...');
152
+ this.socketClient.requestStatus();
153
+ } else {
154
+ console.warn('Cannot refresh sessions: not connected to server');
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Update footer information based on selected session
160
+ */
161
+ updateFooterInfo() {
162
+ console.log('[SESSION-DEBUG] updateFooterInfo called, selectedSessionId:', this.selectedSessionId);
163
+
164
+ const footerSessionEl = document.getElementById('footer-session');
165
+ const footerWorkingDirEl = document.getElementById('footer-working-dir');
166
+ const footerGitBranchEl = document.getElementById('footer-git-branch');
167
+
168
+ if (!footerSessionEl) {
169
+ console.warn('[SESSION-DEBUG] footer-session element not found');
170
+ return;
171
+ }
172
+
173
+ let sessionInfo = 'All Sessions';
174
+ let workingDir = window.dashboard?.workingDirectoryManager?.getDefaultWorkingDir() || process?.cwd?.() || '/Users/masa/Projects/claude-mpm';
175
+ let gitBranch = 'Unknown';
176
+
177
+ console.log('[SESSION-DEBUG] Initial values - sessionInfo:', sessionInfo, 'workingDir:', workingDir, 'gitBranch:', gitBranch);
178
+
179
+ if (this.selectedSessionId === 'current') {
180
+ sessionInfo = this.currentSessionId ?
181
+ `Current: ${this.currentSessionId.substring(0, 8)}...` :
182
+ 'Current: None';
183
+
184
+ // For current session, try to extract info from recent events
185
+ if (this.currentSessionId) {
186
+ const sessionData = this.extractSessionInfoFromEvents(this.currentSessionId);
187
+ workingDir = sessionData.workingDir || window.dashboard?.workingDirectoryManager?.getDefaultWorkingDir() || '/Users/masa/Projects/claude-mpm';
188
+ gitBranch = sessionData.gitBranch || 'Unknown';
189
+ }
190
+ } else if (this.selectedSessionId) {
191
+ const session = this.sessions.get(this.selectedSessionId);
192
+ if (session) {
193
+ sessionInfo = `${this.selectedSessionId.substring(0, 8)}...`;
194
+ workingDir = session.working_directory || session.workingDirectory || '';
195
+ gitBranch = session.git_branch || session.gitBranch || '';
196
+
197
+ // If session doesn't have these values, extract from events
198
+ if (!workingDir || !gitBranch) {
199
+ const sessionData = this.extractSessionInfoFromEvents(this.selectedSessionId);
200
+ workingDir = workingDir || sessionData.workingDir || '.';
201
+ gitBranch = gitBranch || sessionData.gitBranch || '';
202
+ }
203
+ }
204
+ }
205
+
206
+ console.log('[SESSION-DEBUG] Final values before setting footer - sessionInfo:', sessionInfo, 'workingDir:', workingDir, 'gitBranch:', gitBranch);
207
+
208
+ footerSessionEl.textContent = sessionInfo;
209
+ if (footerWorkingDirEl) {
210
+ console.log('[SESSION-DEBUG] Setting footer working dir to:', workingDir);
211
+ footerWorkingDirEl.textContent = workingDir;
212
+ } else {
213
+ console.warn('[SESSION-DEBUG] footer-working-dir element not found');
214
+ }
215
+ if (footerGitBranchEl) {
216
+ console.log('[SESSION-DEBUG] Setting footer git branch to:', gitBranch);
217
+ footerGitBranchEl.textContent = gitBranch;
218
+ } else {
219
+ console.warn('[SESSION-DEBUG] footer-git-branch element not found');
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Extract working directory and git branch from events for a specific session
225
+ * @param {string} sessionId - Session ID to extract info for
226
+ * @returns {Object} Object with workingDir and gitBranch properties
227
+ */
228
+ extractSessionInfoFromEvents(sessionId) {
229
+ let workingDir = '';
230
+ let gitBranch = '';
231
+
232
+ console.log(`[DEBUG] extractSessionInfoFromEvents called for sessionId: ${sessionId}`);
233
+
234
+ // Get events from the socket client
235
+ const socketClient = this.socketClient;
236
+ if (socketClient && socketClient.events) {
237
+ console.log(`[DEBUG] Total events available: ${socketClient.events.length}`);
238
+
239
+ // Look for session start events or recent events with this session ID
240
+ const sessionEvents = socketClient.events.filter(event =>
241
+ event.data && event.data.session_id === sessionId
242
+ );
243
+
244
+ console.log(`[DEBUG] Events matching sessionId ${sessionId}: ${sessionEvents.length}`);
245
+
246
+ // Log a few sample events to see their structure
247
+ if (sessionEvents.length > 0) {
248
+ console.log(`[DEBUG] Sample events for session ${sessionId}:`);
249
+
250
+ // Show first 3 events
251
+ sessionEvents.slice(0, 3).forEach((event, index) => {
252
+ console.log(`[DEBUG] Event ${index + 1}:`, {
253
+ type: event.type,
254
+ timestamp: event.timestamp,
255
+ data_keys: event.data ? Object.keys(event.data) : 'no data',
256
+ full_event: event
257
+ });
258
+ });
259
+
260
+ // Show last 3 events if different from first 3
261
+ if (sessionEvents.length > 3) {
262
+ console.log(`[DEBUG] Last 3 events for session ${sessionId}:`);
263
+ sessionEvents.slice(-3).forEach((event, index) => {
264
+ console.log(`[DEBUG] Last Event ${index + 1}:`, {
265
+ type: event.type,
266
+ timestamp: event.timestamp,
267
+ data_keys: event.data ? Object.keys(event.data) : 'no data',
268
+ full_event: event
269
+ });
270
+ });
271
+ }
272
+ }
273
+
274
+ // Find the most recent event with working directory and git branch info
275
+ for (let i = sessionEvents.length - 1; i >= 0; i--) {
276
+ const event = sessionEvents[i];
277
+ if (event.data) {
278
+ console.log(`[DEBUG] Examining event ${i} data:`, event.data);
279
+
280
+ // Check for working directory info
281
+ if (!workingDir) {
282
+ if (event.data.working_directory) {
283
+ workingDir = event.data.working_directory;
284
+ console.log(`[DEBUG] Found working_directory: ${workingDir}`);
285
+ } else if (event.data.cwd) {
286
+ workingDir = event.data.cwd;
287
+ console.log(`[DEBUG] Found cwd: ${workingDir}`);
288
+ } else if (event.data.instance_info && event.data.instance_info.working_dir) {
289
+ workingDir = event.data.instance_info.working_dir;
290
+ console.log(`[DEBUG] Found instance_info.working_dir: ${workingDir}`);
291
+ }
292
+ }
293
+
294
+ // Check for git branch info - check all possible field names
295
+ if (!gitBranch) {
296
+ const possibleBranchFields = [
297
+ 'git_branch',
298
+ 'gitBranch',
299
+ 'branch',
300
+ 'git.branch',
301
+ 'vcs_branch',
302
+ 'current_branch'
303
+ ];
304
+
305
+ for (const field of possibleBranchFields) {
306
+ if (event.data[field]) {
307
+ gitBranch = event.data[field];
308
+ console.log(`[DEBUG] Found git branch in field '${field}': ${gitBranch}`);
309
+ break;
310
+ }
311
+ }
312
+
313
+ // Check nested locations
314
+ if (!gitBranch) {
315
+ if (event.data.instance_info) {
316
+ console.log(`[DEBUG] Checking instance_info for branch:`, event.data.instance_info);
317
+ for (const field of possibleBranchFields) {
318
+ if (event.data.instance_info[field]) {
319
+ gitBranch = event.data.instance_info[field];
320
+ console.log(`[DEBUG] Found git branch in instance_info.${field}: ${gitBranch}`);
321
+ break;
322
+ }
323
+ }
324
+ }
325
+
326
+ if (!gitBranch && event.data.git) {
327
+ console.log(`[DEBUG] Checking git object:`, event.data.git);
328
+ if (event.data.git.branch) {
329
+ gitBranch = event.data.git.branch;
330
+ console.log(`[DEBUG] Found git branch in git.branch: ${gitBranch}`);
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ // If we have both, we can stop looking
337
+ if (workingDir && gitBranch) {
338
+ console.log(`[DEBUG] Found both workingDir and gitBranch, stopping search`);
339
+ break;
340
+ }
341
+ }
342
+ }
343
+ } else {
344
+ console.log(`[DEBUG] No socket client or events available`);
345
+ }
346
+
347
+ console.log(`[DEBUG] Final results - workingDir: '${workingDir}', gitBranch: '${gitBranch}'`);
348
+ return { workingDir, gitBranch };
349
+ }
350
+
351
+ /**
352
+ * Set current session ID (from server status)
353
+ * @param {string} sessionId - Current session ID
354
+ */
355
+ setCurrentSessionId(sessionId) {
356
+ this.currentSessionId = sessionId;
357
+ this.updateSessionSelect();
358
+ this.updateFooterInfo();
359
+ }
360
+
361
+ /**
362
+ * Add or update a session
363
+ * @param {Object} sessionData - Session data
364
+ */
365
+ addSession(sessionData) {
366
+ if (!sessionData.id) return;
367
+
368
+ const existingSession = this.sessions.get(sessionData.id);
369
+ if (existingSession) {
370
+ // Update existing session
371
+ Object.assign(existingSession, sessionData);
372
+ } else {
373
+ // Add new session
374
+ this.sessions.set(sessionData.id, {
375
+ id: sessionData.id,
376
+ startTime: sessionData.startTime || sessionData.start_time || new Date().toISOString(),
377
+ lastActivity: sessionData.lastActivity || sessionData.last_activity || new Date().toISOString(),
378
+ eventCount: sessionData.eventCount || sessionData.event_count || 0,
379
+ working_directory: sessionData.working_directory || sessionData.workingDirectory || '',
380
+ git_branch: sessionData.git_branch || sessionData.gitBranch || '',
381
+ agent_type: sessionData.agent_type || sessionData.agentType || '',
382
+ ...sessionData
383
+ });
384
+ }
385
+
386
+ this.updateSessionSelect();
387
+ }
388
+
389
+ /**
390
+ * Remove a session
391
+ * @param {string} sessionId - Session ID to remove
392
+ */
393
+ removeSession(sessionId) {
394
+ if (this.sessions.has(sessionId)) {
395
+ this.sessions.delete(sessionId);
396
+
397
+ // If the removed session was selected, reset to all sessions
398
+ if (this.selectedSessionId === sessionId) {
399
+ this.selectedSessionId = '';
400
+ const sessionSelect = document.getElementById('session-select');
401
+ if (sessionSelect) {
402
+ sessionSelect.value = '';
403
+ }
404
+ this.onSessionFilterChanged();
405
+ }
406
+
407
+ this.updateSessionSelect();
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Get current session filter
413
+ * @returns {string} Current session filter
414
+ */
415
+ getCurrentFilter() {
416
+ return this.selectedSessionId;
417
+ }
418
+
419
+ /**
420
+ * Get session information
421
+ * @param {string} sessionId - Session ID
422
+ * @returns {Object|null} Session data or null if not found
423
+ */
424
+ getSession(sessionId) {
425
+ return this.sessions.get(sessionId) || null;
426
+ }
427
+
428
+ /**
429
+ * Get all sessions
430
+ * @returns {Map} All sessions
431
+ */
432
+ getAllSessions() {
433
+ return this.sessions;
434
+ }
435
+
436
+ /**
437
+ * Get current active session ID
438
+ * @returns {string|null} Current session ID
439
+ */
440
+ getCurrentSessionId() {
441
+ return this.currentSessionId;
442
+ }
443
+
444
+ /**
445
+ * Clear all sessions
446
+ */
447
+ clearSessions() {
448
+ this.sessions.clear();
449
+ this.currentSessionId = null;
450
+ this.selectedSessionId = '';
451
+ this.updateSessionSelect();
452
+ this.updateFooterInfo();
453
+ }
454
+
455
+ /**
456
+ * Export session data
457
+ * @returns {Object} Session export data
458
+ */
459
+ exportSessionData() {
460
+ return {
461
+ sessions: Array.from(this.sessions.entries()),
462
+ currentSessionId: this.currentSessionId,
463
+ selectedSessionId: this.selectedSessionId
464
+ };
465
+ }
466
+
467
+ /**
468
+ * Import session data
469
+ * @param {Object} data - Session import data
470
+ */
471
+ importSessionData(data) {
472
+ if (data.sessions && Array.isArray(data.sessions)) {
473
+ this.sessions.clear();
474
+ data.sessions.forEach(([id, sessionData]) => {
475
+ this.sessions.set(id, sessionData);
476
+ });
477
+ }
478
+
479
+ if (data.currentSessionId) {
480
+ this.currentSessionId = data.currentSessionId;
481
+ }
482
+
483
+ if (data.selectedSessionId !== undefined) {
484
+ this.selectedSessionId = data.selectedSessionId;
485
+ }
486
+
487
+ this.updateSessionSelect();
488
+ this.updateFooterInfo();
489
+ }
490
+
491
+ /**
492
+ * Get events for a specific session
493
+ * @param {string} sessionId - Session ID to get events for
494
+ * @returns {Array} - Filtered events for the session
495
+ */
496
+ getEventsForSession(sessionId) {
497
+ if (!sessionId || !this.socketClient) {
498
+ return [];
499
+ }
500
+
501
+ const allEvents = this.socketClient.events || [];
502
+ return allEvents.filter(event => {
503
+ // Check for session ID in various possible locations
504
+ const eventSessionId = event.session_id ||
505
+ (event.data && event.data.session_id) ||
506
+ null;
507
+ return eventSessionId === sessionId;
508
+ });
509
+ }
510
+ }
511
+
512
+ // Global functions for backward compatibility
513
+ window.refreshSessions = function() {
514
+ if (window.sessionManager) {
515
+ window.sessionManager.refreshSessions();
516
+ }
517
+ };
518
+
519
+ // Export for global use
520
+ window.SessionManager = SessionManager;