claude-mpm 4.1.8__py3-none-any.whl → 4.1.11__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 (111) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +26 -1
  3. claude_mpm/agents/agents_metadata.py +57 -0
  4. claude_mpm/agents/templates/.claude-mpm/memories/README.md +17 -0
  5. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +3 -0
  6. claude_mpm/agents/templates/agent-manager.json +263 -17
  7. claude_mpm/agents/templates/agentic_coder_optimizer.json +222 -0
  8. claude_mpm/agents/templates/code_analyzer.json +18 -8
  9. claude_mpm/agents/templates/engineer.json +1 -1
  10. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +39 -0
  11. claude_mpm/agents/templates/qa.json +1 -1
  12. claude_mpm/agents/templates/research.json +1 -1
  13. claude_mpm/cli/__init__.py +15 -0
  14. claude_mpm/cli/commands/__init__.py +6 -0
  15. claude_mpm/cli/commands/analyze.py +548 -0
  16. claude_mpm/cli/commands/analyze_code.py +524 -0
  17. claude_mpm/cli/commands/configure.py +78 -28
  18. claude_mpm/cli/commands/configure_tui.py +62 -60
  19. claude_mpm/cli/commands/dashboard.py +288 -0
  20. claude_mpm/cli/commands/debug.py +1386 -0
  21. claude_mpm/cli/commands/mpm_init.py +427 -0
  22. claude_mpm/cli/commands/mpm_init_handler.py +83 -0
  23. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  24. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  25. claude_mpm/cli/parsers/base_parser.py +44 -0
  26. claude_mpm/cli/parsers/dashboard_parser.py +113 -0
  27. claude_mpm/cli/parsers/debug_parser.py +319 -0
  28. claude_mpm/cli/parsers/mpm_init_parser.py +122 -0
  29. claude_mpm/constants.py +13 -1
  30. claude_mpm/core/framework_loader.py +148 -6
  31. claude_mpm/core/log_manager.py +16 -13
  32. claude_mpm/core/logger.py +1 -1
  33. claude_mpm/core/unified_agent_registry.py +1 -1
  34. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +1 -0
  35. claude_mpm/dashboard/analysis_runner.py +455 -0
  36. claude_mpm/dashboard/static/built/components/activity-tree.js +2 -0
  37. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  38. claude_mpm/dashboard/static/built/components/code-tree.js +2 -0
  39. claude_mpm/dashboard/static/built/components/code-viewer.js +2 -0
  40. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  41. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  42. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  43. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  44. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  45. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  46. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  47. claude_mpm/dashboard/static/css/activity.css +549 -0
  48. claude_mpm/dashboard/static/css/code-tree.css +1175 -0
  49. claude_mpm/dashboard/static/css/dashboard.css +245 -0
  50. claude_mpm/dashboard/static/dist/components/activity-tree.js +2 -0
  51. claude_mpm/dashboard/static/dist/components/code-tree.js +2 -0
  52. claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
  53. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  54. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  55. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  56. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  57. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  58. claude_mpm/dashboard/static/js/components/activity-tree.js +1338 -0
  59. claude_mpm/dashboard/static/js/components/code-tree.js +2535 -0
  60. claude_mpm/dashboard/static/js/components/code-viewer.js +480 -0
  61. claude_mpm/dashboard/static/js/components/event-viewer.js +59 -9
  62. claude_mpm/dashboard/static/js/components/session-manager.js +40 -4
  63. claude_mpm/dashboard/static/js/components/socket-manager.js +12 -0
  64. claude_mpm/dashboard/static/js/components/ui-state-manager.js +4 -0
  65. claude_mpm/dashboard/static/js/components/working-directory.js +17 -1
  66. claude_mpm/dashboard/static/js/dashboard.js +51 -0
  67. claude_mpm/dashboard/static/js/socket-client.js +465 -29
  68. claude_mpm/dashboard/templates/index.html +182 -4
  69. claude_mpm/hooks/claude_hooks/hook_handler.py +182 -5
  70. claude_mpm/hooks/claude_hooks/installer.py +386 -113
  71. claude_mpm/scripts/claude-hook-handler.sh +161 -0
  72. claude_mpm/scripts/socketio_daemon.py +121 -8
  73. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +2 -2
  74. claude_mpm/services/agents/deployment/agent_record_service.py +1 -2
  75. claude_mpm/services/agents/memory/memory_format_service.py +1 -3
  76. claude_mpm/services/cli/agent_cleanup_service.py +1 -5
  77. claude_mpm/services/cli/agent_dependency_service.py +1 -1
  78. claude_mpm/services/cli/agent_validation_service.py +3 -4
  79. claude_mpm/services/cli/dashboard_launcher.py +2 -3
  80. claude_mpm/services/cli/startup_checker.py +0 -11
  81. claude_mpm/services/core/cache_manager.py +1 -3
  82. claude_mpm/services/core/path_resolver.py +1 -4
  83. claude_mpm/services/core/service_container.py +2 -2
  84. claude_mpm/services/diagnostics/checks/instructions_check.py +1 -2
  85. claude_mpm/services/infrastructure/monitoring/__init__.py +11 -11
  86. claude_mpm/services/infrastructure/monitoring.py +11 -11
  87. claude_mpm/services/project/architecture_analyzer.py +1 -1
  88. claude_mpm/services/project/dependency_analyzer.py +4 -4
  89. claude_mpm/services/project/language_analyzer.py +3 -3
  90. claude_mpm/services/project/metrics_collector.py +3 -6
  91. claude_mpm/services/socketio/event_normalizer.py +64 -0
  92. claude_mpm/services/socketio/handlers/__init__.py +2 -0
  93. claude_mpm/services/socketio/handlers/code_analysis.py +672 -0
  94. claude_mpm/services/socketio/handlers/registry.py +2 -0
  95. claude_mpm/services/socketio/server/connection_manager.py +6 -4
  96. claude_mpm/services/socketio/server/core.py +100 -11
  97. claude_mpm/services/socketio/server/main.py +8 -2
  98. claude_mpm/services/visualization/__init__.py +19 -0
  99. claude_mpm/services/visualization/mermaid_generator.py +938 -0
  100. claude_mpm/tools/__main__.py +208 -0
  101. claude_mpm/tools/code_tree_analyzer.py +1596 -0
  102. claude_mpm/tools/code_tree_builder.py +631 -0
  103. claude_mpm/tools/code_tree_events.py +416 -0
  104. claude_mpm/tools/socketio_debug.py +671 -0
  105. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/METADATA +2 -1
  106. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/RECORD +110 -74
  107. claude_mpm/agents/schema/agent_schema.json +0 -314
  108. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/WHEEL +0 -0
  109. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/entry_points.txt +0 -0
  110. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/licenses/LICENSE +0 -0
  111. {claude_mpm-4.1.8.dist-info → claude_mpm-4.1.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,455 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Code Analysis Runner for Dashboard
4
+ ===================================
5
+
6
+ WHY: Manages subprocess execution of code analysis, streaming results to
7
+ Socket.IO clients in real-time while handling cancellation and error recovery.
8
+
9
+ DESIGN DECISIONS:
10
+ - Use subprocess for isolation and cancellation support
11
+ - Stream output line-by-line for real-time updates
12
+ - Queue multiple analysis requests
13
+ - Handle process lifecycle management
14
+ - Convert analyzer events to Socket.IO events
15
+ """
16
+
17
+ import json
18
+ import os
19
+ import subprocess
20
+ import sys
21
+ import threading
22
+ from dataclasses import asdict, dataclass
23
+ from datetime import datetime
24
+ from pathlib import Path
25
+ from queue import Queue
26
+ from typing import Any, Dict, List, Optional
27
+
28
+ from ..core.logging_config import get_logger
29
+
30
+
31
+ @dataclass
32
+ class AnalysisRequest:
33
+ """Represents a code analysis request."""
34
+
35
+ request_id: str
36
+ path: str
37
+ languages: Optional[List[str]] = None
38
+ max_depth: Optional[int] = None
39
+ ignore_patterns: Optional[List[str]] = None
40
+ timestamp: datetime = None
41
+
42
+ def __post_init__(self):
43
+ if self.timestamp is None:
44
+ self.timestamp = datetime.utcnow()
45
+
46
+
47
+ class CodeAnalysisRunner:
48
+ """Manages code analysis subprocess execution for the dashboard.
49
+
50
+ WHY: Provides isolation between the dashboard server and analysis process,
51
+ allowing for cancellation, resource limits, and crash recovery.
52
+ """
53
+
54
+ def __init__(self, socketio_server):
55
+ """Initialize the analysis runner.
56
+
57
+ Args:
58
+ socketio_server: SocketIOServer instance for broadcasting events
59
+ """
60
+ self.logger = get_logger(__name__)
61
+ self.server = socketio_server
62
+ self.current_process = None
63
+ self.current_request = None
64
+ self.request_queue = Queue()
65
+ self.running = False
66
+ self.worker_thread = None
67
+ self.cancel_event = threading.Event()
68
+
69
+ # Statistics
70
+ self.stats = {
71
+ "analyses_started": 0,
72
+ "analyses_completed": 0,
73
+ "analyses_cancelled": 0,
74
+ "analyses_failed": 0,
75
+ "total_files": 0,
76
+ "total_nodes": 0,
77
+ }
78
+
79
+ def start(self):
80
+ """Start the analysis runner worker thread."""
81
+ if self.running:
82
+ return
83
+
84
+ self.running = True
85
+ self.cancel_event.clear()
86
+ self.worker_thread = threading.Thread(target=self._worker_loop, daemon=True)
87
+ self.worker_thread.start()
88
+ self.logger.info("Code analysis runner started")
89
+
90
+ def stop(self):
91
+ """Stop the analysis runner and cleanup."""
92
+ self.running = False
93
+ self.cancel_current()
94
+
95
+ # Add sentinel to queue to wake up worker
96
+ self.request_queue.put(None)
97
+
98
+ if self.worker_thread:
99
+ self.worker_thread.join(timeout=5)
100
+
101
+ self.logger.info("Code analysis runner stopped")
102
+
103
+ def request_analysis(
104
+ self,
105
+ request_id: str,
106
+ path: str,
107
+ languages: Optional[List[str]] = None,
108
+ max_depth: Optional[int] = None,
109
+ ignore_patterns: Optional[List[str]] = None,
110
+ ) -> bool:
111
+ """Queue a new analysis request.
112
+
113
+ Args:
114
+ request_id: Unique request identifier
115
+ path: Directory path to analyze
116
+ languages: Optional list of languages to filter
117
+ max_depth: Optional maximum directory depth
118
+ ignore_patterns: Optional list of patterns to ignore
119
+
120
+ Returns:
121
+ True if request was queued successfully
122
+ """
123
+ # Validate path
124
+ analysis_path = Path(path).resolve()
125
+ if not analysis_path.exists():
126
+ self._emit_error(request_id, f"Path does not exist: {path}")
127
+ return False
128
+
129
+ if not analysis_path.is_dir():
130
+ self._emit_error(request_id, f"Path is not a directory: {path}")
131
+ return False
132
+
133
+ # Create request
134
+ request = AnalysisRequest(
135
+ request_id=request_id,
136
+ path=str(analysis_path),
137
+ languages=languages,
138
+ max_depth=max_depth,
139
+ ignore_patterns=ignore_patterns,
140
+ )
141
+
142
+ # Queue request
143
+ self.request_queue.put(request)
144
+ self.logger.info(f"Queued analysis request {request_id} for {path}")
145
+
146
+ # Emit queued event
147
+ self._emit_event(
148
+ "code:analysis:queued",
149
+ {
150
+ "request_id": request_id,
151
+ "path": str(analysis_path),
152
+ "queue_size": self.request_queue.qsize(),
153
+ },
154
+ )
155
+
156
+ return True
157
+
158
+ def cancel_current(self):
159
+ """Cancel the currently running analysis."""
160
+ if self.current_process and self.current_process.poll() is None:
161
+ self.cancel_event.set()
162
+
163
+ # Try graceful termination first
164
+ self.current_process.terminate()
165
+ try:
166
+ self.current_process.wait(timeout=2)
167
+ except subprocess.TimeoutExpired:
168
+ # Force kill if needed
169
+ self.current_process.kill()
170
+ self.current_process.wait()
171
+
172
+ self.stats["analyses_cancelled"] += 1
173
+
174
+ if self.current_request:
175
+ self._emit_event(
176
+ "code:analysis:cancelled",
177
+ {
178
+ "request_id": self.current_request.request_id,
179
+ "path": self.current_request.path,
180
+ },
181
+ )
182
+
183
+ self.logger.info("Cancelled current analysis")
184
+
185
+ def get_status(self) -> Dict[str, Any]:
186
+ """Get current runner status.
187
+
188
+ Returns:
189
+ Dictionary with current status and statistics
190
+ """
191
+ return {
192
+ "running": self.running,
193
+ "current_request": (
194
+ asdict(self.current_request) if self.current_request else None
195
+ ),
196
+ "queue_size": self.request_queue.qsize(),
197
+ "stats": self.stats.copy(),
198
+ }
199
+
200
+ def _worker_loop(self):
201
+ """Worker thread loop for processing analysis requests."""
202
+ while self.running:
203
+ try:
204
+ # Get next request (blocking with timeout)
205
+ request = self.request_queue.get(timeout=1)
206
+
207
+ if request is None: # Sentinel value
208
+ break
209
+
210
+ # Reset cancel event
211
+ self.cancel_event.clear()
212
+
213
+ # Process request
214
+ self._process_request(request)
215
+
216
+ except Exception as e:
217
+ self.logger.error(f"Error in worker loop: {e}")
218
+
219
+ def _process_request(self, request: AnalysisRequest):
220
+ """Process a single analysis request.
221
+
222
+ Args:
223
+ request: The analysis request to process
224
+ """
225
+ self.current_request = request
226
+ self.stats["analyses_started"] += 1
227
+
228
+ try:
229
+ # Emit start event
230
+ self._emit_event(
231
+ "code:analysis:start",
232
+ {
233
+ "request_id": request.request_id,
234
+ "path": request.path,
235
+ "languages": request.languages,
236
+ "timestamp": request.timestamp.isoformat(),
237
+ },
238
+ )
239
+
240
+ # Build command
241
+ cmd = self._build_command(request)
242
+ self.logger.info(f"Starting analysis subprocess: {' '.join(cmd)}")
243
+
244
+ # Start subprocess
245
+ try:
246
+ self.current_process = subprocess.Popen(
247
+ cmd,
248
+ stdout=subprocess.PIPE,
249
+ stderr=subprocess.PIPE,
250
+ text=True,
251
+ bufsize=1,
252
+ universal_newlines=True,
253
+ env=self._get_subprocess_env(),
254
+ )
255
+ self.logger.debug(
256
+ f"Subprocess started with PID: {self.current_process.pid}"
257
+ )
258
+ except FileNotFoundError as e:
259
+ raise subprocess.SubprocessError(
260
+ f"Python executable not found: {cmd[0]}"
261
+ ) from e
262
+ except Exception as e:
263
+ raise subprocess.SubprocessError(
264
+ f"Failed to start subprocess: {e}"
265
+ ) from e
266
+
267
+ # Process output
268
+ self._process_output(request)
269
+
270
+ # Wait for completion
271
+ return_code = self.current_process.wait()
272
+
273
+ if self.cancel_event.is_set():
274
+ # Analysis was cancelled
275
+ pass # Event already emitted in cancel_current
276
+ elif return_code == 0:
277
+ # Success
278
+ self.stats["analyses_completed"] += 1
279
+ self._emit_event(
280
+ "code:analysis:complete",
281
+ {
282
+ "request_id": request.request_id,
283
+ "path": request.path,
284
+ "stats": {
285
+ "total_files": self.stats["total_files"],
286
+ "total_nodes": self.stats["total_nodes"],
287
+ },
288
+ },
289
+ )
290
+ else:
291
+ # Failure - capture any remaining stderr
292
+ stderr_output = ""
293
+ if self.current_process.stderr:
294
+ try:
295
+ # Read any remaining stderr output
296
+ stderr_lines = []
297
+ for line in self.current_process.stderr:
298
+ stderr_lines.append(line.strip())
299
+ stderr_output = "\n".join(stderr_lines)
300
+ except Exception as e:
301
+ stderr_output = f"Failed to read stderr: {e}"
302
+
303
+ self.stats["analyses_failed"] += 1
304
+ error_msg = f"Analysis failed with code {return_code}"
305
+ if stderr_output:
306
+ error_msg += f": {stderr_output}"
307
+
308
+ self.logger.error(f"Subprocess failed: {error_msg}")
309
+ self._emit_error(request.request_id, error_msg)
310
+
311
+ except subprocess.SubprocessError as e:
312
+ self.logger.error(f"Subprocess error for request {request.request_id}: {e}")
313
+ self.stats["analyses_failed"] += 1
314
+ self._emit_error(request.request_id, f"Failed to start analyzer: {e}")
315
+ except Exception as e:
316
+ self.logger.error(
317
+ f"Error processing request {request.request_id}: {e}", exc_info=True
318
+ )
319
+ self.stats["analyses_failed"] += 1
320
+ self._emit_error(request.request_id, str(e))
321
+
322
+ finally:
323
+ self.current_process = None
324
+ self.current_request = None
325
+
326
+ def _build_command(self, request: AnalysisRequest) -> List[str]:
327
+ """Build the subprocess command for analysis.
328
+
329
+ Args:
330
+ request: The analysis request
331
+
332
+ Returns:
333
+ Command list for subprocess.Popen
334
+ """
335
+ # Get Python executable
336
+ python_exe = sys.executable
337
+
338
+ # Build command - use the CLI analyze-code command
339
+ cmd = [
340
+ python_exe,
341
+ "-m",
342
+ "claude_mpm",
343
+ "analyze-code",
344
+ request.path,
345
+ "--emit-events",
346
+ "--output",
347
+ "json",
348
+ ]
349
+
350
+ # Add optional parameters
351
+ if request.languages:
352
+ cmd.extend(["--languages", ",".join(request.languages)])
353
+
354
+ if request.max_depth:
355
+ cmd.extend(["--max-depth", str(request.max_depth)])
356
+
357
+ if request.ignore_patterns:
358
+ for pattern in request.ignore_patterns:
359
+ cmd.extend(["--ignore", pattern])
360
+
361
+ return cmd
362
+
363
+ def _get_subprocess_env(self) -> Dict[str, str]:
364
+ """Get environment variables for subprocess.
365
+
366
+ Returns:
367
+ Environment dictionary for subprocess
368
+ """
369
+ env = os.environ.copy()
370
+
371
+ # Ensure Socket.IO URL is set for event emission
372
+ env["SOCKETIO_URL"] = f"http://localhost:{self.server.port}"
373
+
374
+ # Set Python path to include our modules
375
+ python_path = env.get("PYTHONPATH", "")
376
+ src_path = str(Path(__file__).parent.parent.parent)
377
+ if src_path not in python_path:
378
+ env["PYTHONPATH"] = f"{src_path}:{python_path}" if python_path else src_path
379
+
380
+ return env
381
+
382
+ def _process_output(self, request: AnalysisRequest):
383
+ """Process subprocess output and emit events.
384
+
385
+ Args:
386
+ request: The current analysis request
387
+ """
388
+ if not self.current_process:
389
+ return
390
+
391
+ # Read output line by line
392
+ for line in iter(self.current_process.stdout.readline, ""):
393
+ if self.cancel_event.is_set():
394
+ break
395
+
396
+ line = line.strip()
397
+ if not line:
398
+ continue
399
+
400
+ try:
401
+ # Parse JSON event
402
+ event = json.loads(line)
403
+
404
+ # Route event to appropriate handler
405
+ event_type = event.get("type")
406
+ event_data = event.get("data", {})
407
+
408
+ # Add request ID to event data
409
+ event_data["request_id"] = request.request_id
410
+
411
+ # Update statistics based on event type
412
+ if event_type == "code:file:complete":
413
+ self.stats["total_files"] += 1
414
+ elif event_type == "code:node:found":
415
+ self.stats["total_nodes"] += 1
416
+
417
+ # Emit to Socket.IO clients
418
+ self._emit_event(event_type, event_data)
419
+
420
+ except json.JSONDecodeError:
421
+ # Not JSON, treat as log message
422
+ self.logger.debug(f"Analyzer output: {line}")
423
+ except Exception as e:
424
+ self.logger.warning(f"Error processing analyzer output: {e}")
425
+
426
+ def _emit_event(self, event_type: str, data: Dict[str, Any]):
427
+ """Emit an event to Socket.IO clients.
428
+
429
+ Args:
430
+ event_type: Type of event
431
+ data: Event data
432
+ """
433
+ if self.server:
434
+ # Add timestamp if not present
435
+ if "timestamp" not in data:
436
+ data["timestamp"] = datetime.utcnow().isoformat()
437
+
438
+ # Broadcast to all clients
439
+ self.server.broadcast_event(event_type, data)
440
+
441
+ def _emit_error(self, request_id: str, message: str):
442
+ """Emit an error event.
443
+
444
+ Args:
445
+ request_id: Request that caused the error
446
+ message: Error message
447
+ """
448
+ self._emit_event(
449
+ "code:analysis:error",
450
+ {
451
+ "request_id": request_id,
452
+ "message": message,
453
+ "timestamp": datetime.utcnow().isoformat(),
454
+ },
455
+ )
@@ -0,0 +1,2 @@
1
+ class t{constructor(){this.container=null,this.svg=null,this.treeData=null,this.root=null,this.treeLayout=null,this.treeGroup=null,this.events=[],this.todoWriteStack=[],this.activeAgent=null,this.activeAgentStack=[],this.margin={top:20,right:120,bottom:20,left:120},this.width=960-this.margin.left-this.margin.right,this.height=500-this.margin.top-this.margin.bottom,this.nodeId=0,this.duration=750,this.timeRange="30min",this.searchTerm="",this.tooltip=null,this.initialized=!1}initialize(){if(console.log("ActivityTree.initialize() called, initialized:",this.initialized),this.initialized)return void console.log("Activity tree already initialized, skipping");if(this.container=document.getElementById("activity-tree-container"),!this.container&&(this.container=document.getElementById("activity-tree"),!this.container))return void console.error("Activity tree container not found in DOM");this.container.textContent&&this.container.textContent.trim()&&(console.log("Clearing existing text content from container:",this.container.textContent),this.container.textContent=""),console.log("Activity tree container found:",this.container);const t=document.getElementById("activity-tab");if(t){if(!t.classList.contains("active"))return console.log("Activity tab not active, initializing but deferring render"),this.container.textContent&&this.container.textContent.trim()&&(this.container.textContent=""),this.setupControls(),this.initializeTreeData(),this.subscribeToEvents(),void(this.initialized=!0);if(this.container.textContent&&this.container.textContent.trim()&&(console.log("Clearing container text before creating visualization"),this.container.textContent=""),this.setupControls(),this.createVisualization(),!this.svg||!this.treeGroup)return console.error("Failed to create D3 visualization elements"),void(this.container&&(this.container.innerHTML='<div style="padding: 20px; text-align: center; color: #e53e3e;">⚠️ Failed to create visualization. Please refresh the page.</div>'));this.initializeTreeData(),this.root?this.update(this.root):console.warn("Root not created, skipping initial update"),this.subscribeToEvents(),this.initialized=!0,console.log("Activity tree initialization complete")}else console.error("Activity tab panel (#activity-tab) not found in DOM")}forceShow(){if(console.log("ActivityTree.forceShow() called"),this.container||(this.container=document.getElementById("activity-tree-container")||document.getElementById("activity-tree"),this.container)){if(this.container.textContent&&this.container.textContent.trim()&&(console.log("Clearing text from container:",this.container.textContent),this.container.innerHTML=""),this.svg||this.createVisualization(),this.root||this.initializeTreeData(),this.root&&this.svg&&this.treeGroup&&this.update(this.root),this.svg){const t=this.svg.node();t&&(t.style.display="block",t.style.visibility="visible")}}else console.error("Cannot find activity tree container")}renderWhenVisible(){if(console.log("ActivityTree.renderWhenVisible() called"),this.container&&this.container.textContent&&this.container.textContent.trim()&&!this.svg&&(console.log("Clearing text content before rendering:",this.container.textContent),this.container.textContent=""),!this.initialized)return console.log("Not initialized yet, calling initialize..."),void this.initialize();this.svg||(console.log("Creating deferred visualization..."),this.createVisualization(),this.svg&&this.treeGroup&&this.root?this.update(this.root):this.root||(console.warn("No root node available, initializing tree data..."),this.initializeTreeData(),this.root&&this.svg&&this.treeGroup&&this.update(this.root))),this.root&&this.svg?(console.log("Updating tree with current data..."),this.update(this.root)):console.warn("Cannot update tree - missing components:",{hasRoot:!!this.root,hasSvg:!!this.svg,hasTreeGroup:!!this.treeGroup})}setupControls(){const t=document.getElementById("expand-all");t&&t.addEventListener("click",()=>this.expandAll());const e=document.getElementById("collapse-all");e&&e.addEventListener("click",()=>this.collapseAll());const i=document.getElementById("reset-zoom");i&&i.addEventListener("click",()=>this.resetZoom());const o=document.getElementById("time-range");o&&o.addEventListener("change",t=>{this.timeRange=t.target.value,this.filterEventsByTime()});const n=document.getElementById("activity-search");n&&n.addEventListener("input",t=>{this.searchTerm=t.target.value.toLowerCase(),this.highlightSearchResults()})}createVisualization(){if("undefined"==typeof d3)return console.error("D3.js is not loaded! Cannot create activity tree visualization."),void(this.container&&(this.container.innerHTML='<div style="padding: 20px; text-align: center; color: #e53e3e;">⚠️ D3.js is not loaded. Cannot create visualization.</div>'));const t=this.container.getBoundingClientRect();this.width=t.width-this.margin.left-this.margin.right,this.height=Math.max(500,t.height-this.margin.top-this.margin.bottom),console.log("Creating D3 visualization with dimensions:",{width:this.width,height:this.height}),d3.select(this.container).selectAll("*").remove(),this.svg=d3.select(this.container).append("svg").attr("width","100%").attr("height","100%").attr("viewBox",`0 0 ${this.width+this.margin.left+this.margin.right} ${this.height+this.margin.top+this.margin.bottom}`),this.treeGroup=this.svg.append("g").attr("class","tree-group").attr("transform",`translate(${this.margin.left},${this.margin.top})`);const e=d3.zoom().scaleExtent([.1,3]).on("zoom",t=>{this.treeGroup.attr("transform",`translate(${this.margin.left+t.transform.x},${this.margin.top+t.transform.y}) scale(${t.transform.k})`)});this.svg.call(e),this.treeLayout=d3.tree().size([this.height,this.width]),console.log("ActivityTree: Tree layout created:",this.treeLayout),this.tooltip=d3.select("body").append("div").attr("class","activity-tooltip").style("opacity",0),console.log("ActivityTree: Visualization complete, svg:",this.svg,"treeGroup:",this.treeGroup)}initializeTreeData(){if(console.log("ActivityTree: Initializing tree data"),this.treeData={name:"PM",type:"pm",icon:"🎯",children:[],_children:null},"undefined"==typeof d3)return console.error("ActivityTree: D3 is not available - cannot create hierarchy!"),void(this.container&&(this.container.innerHTML='<div style="padding: 20px; text-align: center; color: #e53e3e;">⚠️ Waiting for D3.js to load...</div>'));this.root=d3.hierarchy(this.treeData),this.root.x0=this.height/2,this.root.y0=0,console.log("ActivityTree: Root node created:",this.root),this.updateStats()}subscribeToEvents(){if(!window.socketClient)return console.warn("Socket client not available for activity tree"),void setTimeout(()=>this.subscribeToEvents(),1e3);console.log("ActivityTree: Setting up event subscription"),window.socketClient.onEventUpdate(t=>{console.log(`ActivityTree: onEventUpdate called with ${t.length} total events`);const e=t.length-this.events.length;if(e>0){const i=t.slice(this.events.length);console.log(`ActivityTree: Processing ${e} new events`,i),i.forEach(t=>{this.processEvent(t)}),this.events=[...t]}});const t=window.socketClient?.events||window.eventViewer?.events||[];t.length>0?(console.log(`ActivityTree: Processing ${t.length} existing events`,t),t.forEach(t=>{this.processEvent(t)}),this.events=[...t]):(console.log("ActivityTree: No existing events found"),this.events=[])}processEvent(t){if(!t)return void console.log("ActivityTree: Ignoring null event");let e=null;if(t.hook_event_name)e=t.hook_event_name;else if("hook"===t.type&&t.subtype){e={pre_tool:"PreToolUse",post_tool:"PostToolUse",subagent_start:"SubagentStart",subagent_stop:"SubagentStop",todo_write:"TodoWrite"}[t.subtype]}else"todo"===t.type&&"updated"===t.subtype?e="TodoWrite":"subagent"===t.type?"started"===t.subtype?e="SubagentStart":"stopped"===t.subtype&&(e="SubagentStop"):"start"===t.type&&(e="Start");if(!e)return void("hook"!==t.type&&"todo"!==t.type&&"subagent"!==t.type||console.log("ActivityTree: Cannot determine event type for:",t));console.log(`ActivityTree: Processing event: ${e}`,t);const i=new Date(t.timestamp);if(this.isEventInTimeRange(i)){switch(e){case"TodoWrite":this.processTodoWrite(t);break;case"SubagentStart":this.processSubagentStart(t);break;case"SubagentStop":this.processSubagentStop(t);break;case"PreToolUse":this.processToolUse(t);break;case"PostToolUse":this.updateToolStatus(t,"completed");break;case"Start":this.initializeTreeData(),this.update(this.root)}this.updateStats()}}processTodoWrite(t){console.log("ActivityTree: Processing TodoWrite event:",t);let e=t.todos||t.data?.todos||t.data||[];if(e&&"object"==typeof e&&e.todos&&(e=e.todos),!Array.isArray(e))return void console.log("ActivityTree: Invalid todos format in event:",t);if(0===e.length)return void console.log("ActivityTree: No todos in event");const i=e.find(t=>"in_progress"===t.status);if(!i)return void console.log("ActivityTree: No in-progress todo found");console.log("ActivityTree: Found active todo:",i);const o={name:i.activeForm||i.content,type:"todowrite",icon:"📝",content:i.content,status:i.status,timestamp:t.timestamp,children:[],_children:null,eventId:t.id};this.root?this.root.data?(this.root.data.children||(this.root.data.children=[]),console.log("ActivityTree: Adding TodoWrite node to root"),this.root.data.children.push(o),this.todoWriteStack.push({node:o,content:i.content}),console.log("ActivityTree: Calling update with root:",this.root),this.update(this.root),console.log("ActivityTree: Update complete")):console.error("ActivityTree: Root has no data!"):console.error("ActivityTree: No root node!")}processSubagentStart(t){const e=t.agent_name||t.data?.agent_name||t.data?.agent_type||t.agent_type||t.agent||"unknown",i={name:e,type:"agent",icon:this.getAgentIcon(e),timestamp:t.timestamp,children:[],_children:null,eventId:t.id,sessionId:t.session_id||t.data?.session_id};let o=null;if(this.todoWriteStack.length>0){const t=this.todoWriteStack[this.todoWriteStack.length-1];t.content&&t.content.toLowerCase().includes(e.toLowerCase())&&(o=t.node)}o||(o=this.root.data),o.children||(o.children=[]),o.children.push(i),this.activeAgent=i,this.activeAgentStack.push(i),this.update(this.root)}processSubagentStop(t){const e=t.session_id||t.data?.session_id;this.activeAgent&&this.activeAgent.sessionId===e&&(this.activeAgent.status="completed",this.activeAgentStack.pop(),this.activeAgent=this.activeAgentStack.length>0?this.activeAgentStack[this.activeAgentStack.length-1]:null),this.update(this.root)}processToolUse(t){const e=t.tool_name||t.data?.tool_name||t.tool||t.data?.tool||"unknown",i=this.getToolIcon(e),o=t.tool_parameters||t.data?.tool_parameters||t.parameters||t.data?.parameters||{},n={name:e,type:"tool",icon:i,timestamp:t.timestamp,status:"in_progress",children:[],_children:null,eventId:t.id};"Read"===e&&o.file_path?n.children.push({name:o.file_path,type:"file",icon:"📄",timestamp:t.timestamp}):"Edit"===e&&o.file_path?n.children.push({name:o.file_path,type:"file",icon:"✏️",timestamp:t.timestamp}):"Write"===e&&o.file_path?n.children.push({name:o.file_path,type:"file",icon:"💾",timestamp:t.timestamp}):"Bash"===e&&o.command?n.children.push({name:o.command.substring(0,50)+(o.command.length>50?"...":""),type:"command",icon:"⚡",timestamp:t.timestamp}):"WebFetch"===e&&o.url&&n.children.push({name:o.url,type:"url",icon:"🌐",timestamp:t.timestamp});let s=this.activeAgent||this.root.data;s.children||(s.children=[]),s.children.push(n),this.update(this.root)}updateToolStatus(t,e){const i=o=>{if(o.eventId===t.id)return o.status=e,!0;if(o.children)for(let t of o.children)if(i(t))return!0;if(o._children)for(let t of o._children)if(i(t))return!0;return!1};i(this.root.data),this.update(this.root)}getAgentIcon(t){return{engineer:"👷",research:"🔬",qa:"🧪",ops:"⚙️",pm:"📊",architect:"🏗️"}[t.toLowerCase()]||"🤖"}getToolIcon(t){return{read:"👁️",write:"✍️",edit:"✏️",bash:"💻",webfetch:"🌐",grep:"🔍",glob:"📂",todowrite:"📝"}[t.toLowerCase()]||"🔧"}update(t){if(console.log("ActivityTree: update() called with source:",t),"undefined"==typeof d3)return void console.error("ActivityTree: Cannot update - D3.js not loaded");if(!this.svg||!this.treeGroup){if(console.warn("ActivityTree: Cannot update - SVG not initialized"),!this.container)return;if(console.log("Attempting to create visualization from update()"),this.createVisualization(),!this.svg||!this.treeGroup)return void console.error("Failed to create visualization in update()")}if(!this.treeLayout){if(console.warn("ActivityTree: Cannot update - tree layout not initialized"),"undefined"==typeof d3)return;this.treeLayout=d3.tree().size([this.height,this.width]),console.log("Created tree layout in update()")}if(!t||!t.data)return void console.error("ActivityTree: Invalid source in update()",t);if(!this.root)return void console.error("ActivityTree: No root node available for update");let e;try{e=this.treeLayout(this.root)}catch(c){return void console.error("ActivityTree: Error computing tree layout:",c)}const i=e.descendants(),o=e.links();if(console.log(`ActivityTree: Updating tree with ${i.length} nodes`),1===i.length&&this.container){this.container.querySelector("svg")||console.warn("SVG element not found in container after update")}i.forEach(t=>{t.y=180*t.depth});const n=this.treeGroup.selectAll("g.node").data(i,t=>t.id||(t.id=++this.nodeId)),s=n.enter().append("g").attr("class","node").attr("transform",e=>`translate(${t.y0},${t.x0})`).on("click",(t,e)=>this.click(e));s.append("circle").attr("class",t=>`node-circle ${t.data.type}`).attr("r",1e-6).style("fill",t=>t._children?this.getNodeColor(t.data.type):"#fff").style("stroke",t=>this.getNodeColor(t.data.type)),s.append("text").attr("class","node-icon").attr("dy",".35em").attr("text-anchor","middle").style("font-size","14px").text(t=>t.data.icon||""),s.append("text").attr("class","node-label").attr("dy",".35em").attr("x",t=>t.children||t._children?-25:25).attr("text-anchor",t=>t.children||t._children?"end":"start").text(t=>t.data.name).style("fill-opacity",1e-6),s.on("mouseover",(t,e)=>this.showTooltip(t,e)).on("mouseout",()=>this.hideTooltip());const r=s.merge(n);r.transition().duration(this.duration).attr("transform",t=>`translate(${t.y},${t.x})`),r.select("circle.node-circle").attr("r",10).style("fill",t=>"in_progress"===t.data.status||t._children?this.getNodeColor(t.data.type):"#fff").attr("class",t=>{let e=`node-circle ${t.data.type}`;return"in_progress"===t.data.status&&(e+=" pulsing"),"failed"===t.data.status&&(e+=" failed"),e}),r.select("text.node-label").style("fill-opacity",1);const a=n.exit().transition().duration(this.duration).attr("transform",e=>`translate(${t.y},${t.x})`).remove();a.select("circle").attr("r",1e-6),a.select("text").style("fill-opacity",1e-6);const l=this.treeGroup.selectAll("path.link").data(o,t=>t.target.id);l.enter().insert("path","g").attr("class","link").attr("d",e=>{const i={x:t.x0,y:t.y0};return this.diagonal({source:i,target:i})}).merge(l).transition().duration(this.duration).attr("d",this.diagonal),l.exit().transition().duration(this.duration).attr("d",e=>{const i={x:t.x,y:t.y};return this.diagonal({source:i,target:i})}).remove(),i.forEach(t=>{t.x0=t.x,t.y0=t.y}),this.updateBreadcrumb(t)}diagonal(t){return`M ${t.source.y} ${t.source.x}\n C ${(t.source.y+t.target.y)/2} ${t.source.x},\n ${(t.source.y+t.target.y)/2} ${t.target.x},\n ${t.target.y} ${t.target.x}`}click(t){t.children?(t._children=t.children,t.children=null):(t.children=t._children,t._children=null),this.update(t),this.updateBreadcrumb(t)}getNodeColor(t){return{pm:"#4299e1",todowrite:"#48bb78",agent:"#ed8936",tool:"#9f7aea",file:"#38b2ac",command:"#f56565",url:"#4299e1"}[t]||"#718096"}showTooltip(t,e){const i=`\n <strong>${e.data.name}</strong><br>\n Type: ${e.data.type}<br>\n ${e.data.timestamp?`Time: ${new Date(e.data.timestamp).toLocaleTimeString()}`:""}\n ${e.data.status?`<br>Status: ${e.data.status}`:""}\n `;this.tooltip.transition().duration(200).style("opacity",.9),this.tooltip.html(i).style("left",t.pageX+10+"px").style("top",t.pageY-28+"px")}hideTooltip(){this.tooltip.transition().duration(500).style("opacity",0)}expandAll(){const t=e=>{e._children&&(e.children=e._children,e._children=null),e.children&&e.children.forEach(t)};t(this.root),this.update(this.root)}collapseAll(){const t=e=>{e.children&&(e._children=e.children,e._children.forEach(t),e.children=null)};this.root.children?.forEach(t),this.update(this.root)}resetZoom(){if(!this.svg)return void console.warn("Cannot reset zoom: SVG not initialized");const t=d3.zoom().scaleExtent([.1,3]).on("zoom",t=>{this.treeGroup.attr("transform",`translate(${this.margin.left+t.transform.x},${this.margin.top+t.transform.y}) scale(${t.transform.k})`)});this.svg.transition().duration(750).call(t.transform,d3.zoomIdentity),this.treeGroup.transition().duration(750).attr("transform",`translate(${this.margin.left},${this.margin.top})`)}isEventInTimeRange(t){if("all"===this.timeRange)return!0;const e=(new Date-t)/6e4;switch(this.timeRange){case"10min":return e<=10;case"30min":return e<=30;case"hour":return e<=60;default:return!0}}filterEventsByTime(){this.initializeTreeData(),window.eventViewer&&window.eventViewer.events&&window.eventViewer.events.forEach(t=>{this.processEvent(t)})}updateStats(){if(!this.root||!this.root.data){console.warn("ActivityTree: Cannot update stats - root not initialized");const t=document.getElementById("node-count"),e=document.getElementById("active-count"),i=document.getElementById("tree-depth");return t&&(t.textContent="1"),e&&(e.textContent="0"),void(i&&(i.textContent="0"))}const t=this.countNodes(this.root),e=this.countActiveNodes(this.root.data),i=this.getTreeDepth(this.root),o=document.getElementById("node-count"),n=document.getElementById("active-count"),s=document.getElementById("tree-depth");o&&(o.textContent=t),n&&(n.textContent=e),s&&(s.textContent=i),console.log(`ActivityTree: Stats updated - Nodes: ${t}, Active: ${e}, Depth: ${i}`)}countNodes(t){let e=1;return t.children&&t.children.forEach(t=>{e+=this.countNodes(t)}),t._children&&t._children.forEach(t=>{e+=this.countNodes(t)}),e}countActiveNodes(t){let e="in_progress"===t.status?1:0;return t.children&&t.children.forEach(t=>{e+=this.countActiveNodes(t)}),t._children&&t._children.forEach(t=>{e+=this.countActiveNodes(t)}),e}getTreeDepth(t){if(!t.children&&!t._children)return 0;const e=(t.children||t._children).map(t=>this.getTreeDepth(t));return Math.max(...e)+1}updateBreadcrumb(t){const e=[];let i=t;for(;i;)e.unshift(i.data.name),i=i.parent;const o=document.getElementById("activity-breadcrumb");o&&(o.textContent=e.join(" > "))}highlightSearchResults(){this.treeGroup.selectAll(".node-label").style("font-weight","normal").style("fill","#2d3748"),this.searchTerm&&this.treeGroup.selectAll(".node-label").style("font-weight",t=>t.data.name.toLowerCase().includes(this.searchTerm)?"bold":"normal").style("fill",t=>t.data.name.toLowerCase().includes(this.searchTerm)?"#e53e3e":"#2d3748")}}window.ActivityTree=t;const e=()=>{let e=null;const i=()=>{e||(console.log("Creating new Activity Tree instance..."),e=new t,window.activityTreeInstance=e);const i=document.getElementById("activity-tree-container")||document.getElementById("activity-tree");i&&i.textContent&&i.textContent.trim()&&(console.log("Clearing text from activity tree container before init:",i.textContent),i.textContent=""),setTimeout(()=>{console.log("Attempting to initialize Activity Tree visualization..."),e.initialize()},100)};document.querySelectorAll(".tab-button").forEach(t=>{t.addEventListener("click",t=>{"activity"===t.target.getAttribute("data-tab")&&(console.log("Activity tab button clicked, initializing tree..."),i(),e&&setTimeout(()=>{e.renderWhenVisible(),e.forceShow()},150))})}),document.addEventListener("tabChanged",t=>{t.detail&&"activity"===t.detail.newTab&&(console.log("Tab changed to activity, initializing tree..."),i(),e&&setTimeout(()=>{e.renderWhenVisible(),e.forceShow()},150))});const o=document.querySelector(".tab-button.active");o&&"activity"===o.getAttribute("data-tab")&&(console.log("Activity tab is active on load, initializing tree..."),i());const n=document.getElementById("activity-tab");n&&n.classList.contains("active")&&(console.log("Activity panel is active on load, initializing tree..."),e||i()),window.activityTree=()=>e};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",e):e();
2
+ //# sourceMappingURL=activity-tree.js.map
@@ -1,2 +1,2 @@
1
- class e{constructor(e){this.eventViewer=e,this.state={currentDelegation:null,sessionAgents:new Map,eventAgentMap:new Map,pmDelegations:new Map,agentToDelegation:new Map},console.log("Agent inference system initialized")}initialize(){this.state={currentDelegation:null,sessionAgents:new Map,eventAgentMap:new Map,pmDelegations:new Map,agentToDelegation:new Map}}inferAgentFromEvent(e){const t=e.data||{},n=e.session_id||t.session_id||"unknown",a=e.hook_event_name||t.hook_event_name||e.type||"",s=e.subtype||t.subtype||"",i=e.tool_name||t.tool_name||"";if(Math.random()<.1&&console.log("Agent inference debug:",{eventType:a,toolName:i,hasData:!!e.data,dataKeys:Object.keys(t),eventKeys:Object.keys(e),agentType:e.agent_type||t.agent_type,subagentType:e.subagent_type||t.subagent_type}),"SubagentStop"===a||"subagent_stop"===s){const i=this.extractAgentNameFromEvent(e);return console.log("SubagentStop event detected:",{agentName:i,sessionId:n,eventType:a,subtype:s,rawAgentType:e.agent_type||t.agent_type}),{type:"subagent",confidence:"definitive",agentName:i,reason:"SubagentStop event"}}if("Stop"===a||"stop"===s)return{type:"main_agent",confidence:"definitive",agentName:"PM",reason:"Stop event"};if("Task"===i){const t=this.extractSubagentTypeFromTask(e);if(t)return console.log("Task delegation detected:",{agentName:t,sessionId:n,eventType:a}),{type:"subagent",confidence:"high",agentName:t,reason:"Task tool with subagent_type"}}if("PreToolUse"===a&&"Task"===i){const t=this.extractSubagentTypeFromTask(e);if(t)return{type:"subagent",confidence:"high",agentName:t,reason:"PreToolUse Task delegation"}}{const e=n.toLowerCase();if(["subagent","task","agent-"].some(t=>e.includes(t)))return{type:"subagent",confidence:"medium",agentName:"Subagent",reason:"Session ID pattern"}}const o=e.agent_type||t.agent_type||e.agent_id||t.agent_id,g=e.subagent_type||t.subagent_type;if(g&&"unknown"!==g)return{type:"subagent",confidence:"high",agentName:this.normalizeAgentName(g),reason:"subagent_type field"};if(o&&"unknown"!==o&&"main"!==o)return{type:"subagent",confidence:"medium",agentName:this.normalizeAgentName(o),reason:"agent_type field"};if(t.delegation_details?.agent_type)return{type:"subagent",confidence:"high",agentName:this.normalizeAgentName(t.delegation_details.agent_type),reason:"delegation_details"};if(e.type&&e.type.startsWith("hook.")){const a=e.type.replace("hook.","");if("subagent_start"===a||"SubagentStart"===t.hook_event_name){const e=t.agent_type||t.agent_id||"Subagent";return console.log("SubagentStart event from Socket.IO:",{agentName:e,sessionId:n,hookType:a}),{type:"subagent",confidence:"definitive",agentName:this.normalizeAgentName(e),reason:"Socket.IO hook SubagentStart"}}if("subagent_stop"===a||"SubagentStop"===t.hook_event_name){const e=t.agent_type||t.agent_id||"Subagent";return{type:"subagent",confidence:"high",agentName:this.normalizeAgentName(e),reason:"Socket.IO hook SubagentStop"}}}return{type:"main_agent",confidence:"default",agentName:"PM",reason:"default classification"}}normalizeAgentName(e){if(!e)return"Unknown";const t={engineer:"Engineer Agent",research:"Research Agent",qa:"QA Agent",documentation:"Documentation Agent",security:"Security Agent",ops:"Ops Agent",version_control:"Version Control Agent",data_engineer:"Data Engineer Agent",test_integration:"Test Integration Agent",pm:"PM Agent"}[e.toLowerCase()];if(t)return t;let n=e.replace(/_/g," ").split(" ").map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(" ");return n.toLowerCase().includes("agent")||(n+=" Agent"),n}extractSubagentTypeFromTask(e){let t=null;return e.tool_parameters?.subagent_type?t=e.tool_parameters.subagent_type:e.data?.tool_parameters?.subagent_type?t=e.data.tool_parameters.subagent_type:e.data?.delegation_details?.agent_type?t=e.data.delegation_details.agent_type:e.tool_input?.subagent_type&&(t=e.tool_input.subagent_type),t?this.normalizeAgentName(t):null}extractAgentNameFromEvent(e){const t=e.data||{};if("Task"===e.tool_name||"Task"===t.tool_name){const t=this.extractSubagentTypeFromTask(e);if(t)return t}return e.subagent_type&&"unknown"!==e.subagent_type?this.normalizeAgentName(e.subagent_type):t.subagent_type&&"unknown"!==t.subagent_type?this.normalizeAgentName(t.subagent_type):t.delegation_details?.agent_type&&"unknown"!==t.delegation_details.agent_type?this.normalizeAgentName(t.delegation_details.agent_type):e.agent_type&&!["main","unknown"].includes(e.agent_type)?this.normalizeAgentName(e.agent_type):t.agent_type&&!["main","unknown"].includes(t.agent_type)?this.normalizeAgentName(t.agent_type):e.agent_id&&!["main","unknown"].includes(e.agent_id)?this.normalizeAgentName(e.agent_id):t.agent_id&&!["main","unknown"].includes(t.agent_id)?this.normalizeAgentName(t.agent_id):e.agent&&"unknown"!==e.agent?this.normalizeAgentName(e.agent):e.name&&"unknown"!==e.name?this.normalizeAgentName(e.name):"Unknown"}processAgentInference(){const e=this.eventViewer.events;this.state.currentDelegation=null,this.state.sessionAgents.clear(),this.state.eventAgentMap.clear(),this.state.pmDelegations.clear(),this.state.agentToDelegation.clear(),console.log("Processing agent inference for",e.length,"events"),e&&0!==e.length?(e.forEach((e,t)=>{let n;try{const a=this.inferAgentFromEvent(e),s=e.session_id||e.data?.session_id||"default";if(n=a,this.state.currentDelegation&&"default"===a.confidence&&s===this.state.currentDelegation.sessionId&&(n={type:"subagent",confidence:"inherited",agentName:this.state.currentDelegation.agentName,reason:"inherited from delegation context"}),"Task"===e.tool_name&&"subagent"===a.type){const n=`pm_${s}_${t}_${a.agentName}`,i={id:n,agentName:a.agentName,sessionId:s,startIndex:t,endIndex:null,pmCall:e,timestamp:e.timestamp,agentEvents:[]};this.state.pmDelegations.set(n,i),this.state.agentToDelegation.set(a.agentName,n),this.state.currentDelegation={agentName:a.agentName,sessionId:s,startIndex:t,endIndex:null,delegationId:n},console.log("Delegation started:",this.state.currentDelegation)}else if("definitive"===a.confidence&&"SubagentStop event"===a.reason&&this.state.currentDelegation){this.state.currentDelegation.endIndex=t;const e=this.state.pmDelegations.get(this.state.currentDelegation.delegationId);e&&(e.endIndex=t),console.log("Delegation ended:",this.state.currentDelegation),this.state.currentDelegation=null}if(this.state.currentDelegation&&"subagent"===n.type){const a=this.state.pmDelegations.get(this.state.currentDelegation.delegationId);a&&a.agentEvents.push({eventIndex:t,event:e,inference:n})}this.state.eventAgentMap.set(t,n),this.state.sessionAgents.set(s,n),t<5&&console.log(`Event ${t} agent inference:`,{event_type:e.type||e.hook_event_name,subtype:e.subtype,tool_name:e.tool_name,inference:n,hasData:!!e.data,agentType:e.agent_type||e.data?.agent_type})}catch(a){console.error(`Error processing event ${t} for agent inference:`,a),n||(n={type:"main_agent",confidence:"error",agentName:"PM",reason:"error during processing"}),this.state.eventAgentMap.set(t,n)}}),console.log("Agent inference processing complete. Results:",{total_events:e.length,inferred_agents:this.state.eventAgentMap.size,unique_sessions:this.state.sessionAgents.size,pm_delegations:this.state.pmDelegations.size,agent_to_delegation_mappings:this.state.agentToDelegation.size})):console.log("No events to process for agent inference")}getInferredAgent(e){return this.state.eventAgentMap.get(e)||null}getInferredAgentForEvent(e){const t=this.eventViewer.events;let n=t.indexOf(e);if(-1===n&&e.timestamp&&(n=t.findIndex(t=>t.timestamp===e.timestamp&&t.session_id===e.session_id)),-1===n)return console.log("Agent inference: Could not find event in events array, performing inline inference"),this.inferAgentFromEvent(e);let a=this.getInferredAgent(n);return a||(a=this.inferAgentFromEvent(e),this.state.eventAgentMap.set(n,a)),a}getCurrentDelegation(){return this.state.currentDelegation}getSessionAgents(){return this.state.sessionAgents}getEventAgentMap(){return this.state.eventAgentMap}getPMDelegations(){return this.state.pmDelegations}getAgentToDelegationMap(){return this.state.agentToDelegation}getUniqueAgentInstances(){const e=new Map;for(const[a,s]of this.state.pmDelegations){const t=s.agentName;e.has(t)||e.set(t,{id:`consolidated_${t}`,type:"consolidated_agent",agentName:t,delegations:[],pmCalls:[],allEvents:[],firstTimestamp:s.timestamp,lastTimestamp:s.timestamp,totalEventCount:s.agentEvents.length,delegationCount:1});const n=e.get(t);n.delegations.push({id:a,pmCall:s.pmCall,timestamp:s.timestamp,eventCount:s.agentEvents.length,startIndex:s.startIndex,endIndex:s.endIndex,events:s.agentEvents}),s.pmCall&&n.pmCalls.push(s.pmCall),n.allEvents=n.allEvents.concat(s.agentEvents),new Date(s.timestamp)<new Date(n.firstTimestamp)&&(n.firstTimestamp=s.timestamp),new Date(s.timestamp)>new Date(n.lastTimestamp)&&(n.lastTimestamp=s.timestamp),n.totalEventCount+=s.agentEvents.length,n.delegationCount++}const t=this.eventViewer.events;for(let a=0;a<t.length;a++){const n=this.getInferredAgent(a);n&&"subagent"===n.type&&!e.has(n.agentName)&&e.set(n.agentName,{id:`consolidated_${n.agentName}`,type:"consolidated_agent",agentName:n.agentName,delegations:[{id:`implied_pm_${n.agentName}_${a}`,pmCall:null,timestamp:t[a].timestamp,eventCount:1,startIndex:a,endIndex:null,events:[{eventIndex:a,event:t[a],inference:n}]}],pmCalls:[],allEvents:[{eventIndex:a,event:t[a],inference:n}],firstTimestamp:t[a].timestamp,lastTimestamp:t[a].timestamp,totalEventCount:1,delegationCount:1,isImplied:!0})}const n=Array.from(e.values()).sort((e,t)=>new Date(e.firstTimestamp)-new Date(t.firstTimestamp));return console.log("Consolidated unique agents:",{total_unique_agents:n.length,agents:n.map(e=>({name:e.agentName,delegations:e.delegationCount,totalEvents:e.totalEventCount}))}),n}}export{e as A};
1
+ class e{constructor(e){this.eventViewer=e,this.state={currentDelegation:null,sessionAgents:new Map,eventAgentMap:new Map,pmDelegations:new Map,agentToDelegation:new Map,orphanSubagents:new Map,subagentStartEvents:new Map},console.log("Agent inference system initialized")}initialize(){this.state={currentDelegation:null,sessionAgents:new Map,eventAgentMap:new Map,pmDelegations:new Map,agentToDelegation:new Map,orphanSubagents:new Map,subagentStartEvents:new Map}}inferAgentFromEvent(e){const t=e.data||{},n=e.session_id||t.session_id||"unknown",a=e.hook_event_name||t.hook_event_name||e.type||"",s=e.subtype||t.subtype||"",i=e.tool_name||t.tool_name||"";if(Math.random()<.1&&console.log("Agent inference debug:",{eventType:a,toolName:i,hasData:!!e.data,dataKeys:Object.keys(t),eventKeys:Object.keys(e),agentType:e.agent_type||t.agent_type,subagentType:e.subagent_type||t.subagent_type}),"SubagentStop"===a||"subagent_stop"===s){const i=this.extractAgentNameFromEvent(e);return console.log("SubagentStop event detected:",{agentName:i,sessionId:n,eventType:a,subtype:s,rawAgentType:e.agent_type||t.agent_type}),{type:"subagent",confidence:"definitive",agentName:i,reason:"SubagentStop event"}}if("Stop"===a||"stop"===s)return{type:"main_agent",confidence:"definitive",agentName:"PM",reason:"Stop event"};if("Task"===i){const t=this.extractSubagentTypeFromTask(e);if(t)return console.log("Task delegation detected:",{agentName:t,sessionId:n,eventType:a}),{type:"subagent",confidence:"high",agentName:t,reason:"Task tool with subagent_type"}}if("PreToolUse"===a&&"Task"===i){const t=this.extractSubagentTypeFromTask(e);if(t)return{type:"subagent",confidence:"high",agentName:t,reason:"PreToolUse Task delegation"}}{const e=n.toLowerCase();if(["subagent","task","agent-"].some(t=>e.includes(t)))return{type:"subagent",confidence:"medium",agentName:"Subagent",reason:"Session ID pattern"}}const o=e.agent_type||t.agent_type||e.agent_id||t.agent_id,g=e.subagent_type||t.subagent_type;if(g&&"unknown"!==g)return{type:"subagent",confidence:"high",agentName:this.normalizeAgentName(g),reason:"subagent_type field"};if(o&&"unknown"!==o&&"main"!==o)return{type:"subagent",confidence:"medium",agentName:this.normalizeAgentName(o),reason:"agent_type field"};if(t.delegation_details?.agent_type)return{type:"subagent",confidence:"high",agentName:this.normalizeAgentName(t.delegation_details.agent_type),reason:"delegation_details"};if(e.type&&e.type.startsWith("hook.")){const a=e.type.replace("hook.","");if("subagent_start"===a||"SubagentStart"===t.hook_event_name){const e=t.agent_type||t.agent_id||"Subagent";return console.log("SubagentStart event from Socket.IO:",{agentName:e,sessionId:n,hookType:a}),{type:"subagent",confidence:"definitive",agentName:this.normalizeAgentName(e),reason:"Socket.IO hook SubagentStart"}}if("subagent_stop"===a||"SubagentStop"===t.hook_event_name){const e=t.agent_type||t.agent_id||"Subagent";return{type:"subagent",confidence:"high",agentName:this.normalizeAgentName(e),reason:"Socket.IO hook SubagentStop"}}}return{type:"main_agent",confidence:"default",agentName:"PM",reason:"default classification"}}normalizeAgentName(e){if(!e)return"Unknown";const t={engineer:"Engineer Agent",research:"Research Agent",qa:"QA Agent",documentation:"Documentation Agent",security:"Security Agent",ops:"Ops Agent",version_control:"Version Control Agent",data_engineer:"Data Engineer Agent",test_integration:"Test Integration Agent",pm:"PM Agent"}[e.toLowerCase()];if(t)return t;let n=e.replace(/_/g," ").split(" ").map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(" ");return n.toLowerCase().includes("agent")||(n+=" Agent"),n}extractSubagentTypeFromTask(e){let t=null;return e.tool_parameters?.subagent_type?t=e.tool_parameters.subagent_type:e.data?.tool_parameters?.subagent_type?t=e.data.tool_parameters.subagent_type:e.data?.delegation_details?.agent_type?t=e.data.delegation_details.agent_type:e.tool_input?.subagent_type&&(t=e.tool_input.subagent_type),t?this.normalizeAgentName(t):null}extractAgentNameFromEvent(e){const t=e.data||{};if("Task"===e.tool_name||"Task"===t.tool_name){const t=this.extractSubagentTypeFromTask(e);if(t)return t}return e.subagent_type&&"unknown"!==e.subagent_type?this.normalizeAgentName(e.subagent_type):t.subagent_type&&"unknown"!==t.subagent_type?this.normalizeAgentName(t.subagent_type):t.delegation_details?.agent_type&&"unknown"!==t.delegation_details.agent_type?this.normalizeAgentName(t.delegation_details.agent_type):e.agent_type&&!["main","unknown"].includes(e.agent_type)?this.normalizeAgentName(e.agent_type):t.agent_type&&!["main","unknown"].includes(t.agent_type)?this.normalizeAgentName(t.agent_type):e.agent_id&&!["main","unknown"].includes(e.agent_id)?this.normalizeAgentName(e.agent_id):t.agent_id&&!["main","unknown"].includes(t.agent_id)?this.normalizeAgentName(t.agent_id):e.agent&&"unknown"!==e.agent?this.normalizeAgentName(e.agent):e.name&&"unknown"!==e.name?this.normalizeAgentName(e.name):"Unknown"}processAgentInference(){const e=this.eventViewer.events;this.state.currentDelegation=null,this.state.sessionAgents.clear(),this.state.eventAgentMap.clear(),this.state.pmDelegations.clear(),this.state.agentToDelegation.clear(),this.state.orphanSubagents.clear(),this.state.subagentStartEvents.clear(),console.log("Processing agent inference for",e.length,"events"),e&&0!==e.length?(e.forEach((e,t)=>{let n;try{const a=this.inferAgentFromEvent(e),s=e.session_id||e.data?.session_id||"default";n=a,this.state.currentDelegation&&"default"===a.confidence&&s===this.state.currentDelegation.sessionId&&(n={type:"subagent",confidence:"inherited",agentName:this.state.currentDelegation.agentName,reason:"inherited from delegation context"});const i=e.hook_event_name||e.data?.hook_event_name||"";if(("SubagentStart"===i||"hook.subagent_start"===e.type||"subagent_start"===e.subtype)&&"subagent"===a.type&&(this.state.subagentStartEvents.has(a.agentName)||this.state.subagentStartEvents.set(a.agentName,[]),this.state.subagentStartEvents.get(a.agentName).push({eventIndex:t,event:e,timestamp:e.timestamp,sessionId:s})),"Task"===e.tool_name&&"subagent"===a.type){const n=`pm_${s}_${t}_${a.agentName}`,i={id:n,agentName:a.agentName,sessionId:s,startIndex:t,endIndex:null,pmCall:e,timestamp:e.timestamp,agentEvents:[]};this.state.pmDelegations.set(n,i),this.state.agentToDelegation.set(a.agentName,n),this.state.currentDelegation={agentName:a.agentName,sessionId:s,startIndex:t,endIndex:null,delegationId:n},console.log("Delegation started:",this.state.currentDelegation)}else if("definitive"===a.confidence&&"SubagentStop event"===a.reason&&this.state.currentDelegation){this.state.currentDelegation.endIndex=t;const e=this.state.pmDelegations.get(this.state.currentDelegation.delegationId);e&&(e.endIndex=t),console.log("Delegation ended:",this.state.currentDelegation),this.state.currentDelegation=null}if(this.state.currentDelegation&&"subagent"===n.type){const a=this.state.pmDelegations.get(this.state.currentDelegation.delegationId);a&&a.agentEvents.push({eventIndex:t,event:e,inference:n})}this.state.eventAgentMap.set(t,n),this.state.sessionAgents.set(s,n),t<5&&console.log(`Event ${t} agent inference:`,{event_type:e.type||e.hook_event_name,subtype:e.subtype,tool_name:e.tool_name,inference:n,hasData:!!e.data,agentType:e.agent_type||e.data?.agent_type})}catch(a){console.error(`Error processing event ${t} for agent inference:`,a),n||(n={type:"main_agent",confidence:"error",agentName:"PM",reason:"error during processing"}),this.state.eventAgentMap.set(t,n)}}),this.identifyOrphanSubagents(e),console.log("Agent inference processing complete. Results:",{total_events:e.length,inferred_agents:this.state.eventAgentMap.size,unique_sessions:this.state.sessionAgents.size,pm_delegations:this.state.pmDelegations.size,agent_to_delegation_mappings:this.state.agentToDelegation.size,orphan_subagents:this.state.orphanSubagents.size})):console.log("No events to process for agent inference")}getInferredAgent(e){return this.state.eventAgentMap.get(e)||null}getInferredAgentForEvent(e){const t=this.eventViewer.events;let n=t.indexOf(e);if(-1===n&&e.timestamp&&(n=t.findIndex(t=>t.timestamp===e.timestamp&&t.session_id===e.session_id)),-1===n)return console.log("Agent inference: Could not find event in events array, performing inline inference"),this.inferAgentFromEvent(e);let a=this.getInferredAgent(n);return a||(a=this.inferAgentFromEvent(e),this.state.eventAgentMap.set(n,a)),a}getCurrentDelegation(){return this.state.currentDelegation}getSessionAgents(){return this.state.sessionAgents}getEventAgentMap(){return this.state.eventAgentMap}getPMDelegations(){return this.state.pmDelegations}getAgentToDelegationMap(){return this.state.agentToDelegation}buildDelegationHierarchy(){const e=this.getPMDelegations(),t=this.eventViewer.events,n={mainPM:{type:"pm",name:"PM",delegations:[],ownEvents:[],totalEvents:0},impliedPM:{type:"pm_implied",name:"Implied PM",delegations:[],ownEvents:[],totalEvents:0}};for(const[s,i]of e)n.mainPM.delegations.push({id:s,agentName:i.agentName,taskContext:this.extractTaskContext(i.pmCall),events:i.agentEvents,startTime:i.timestamp,endTime:i.endIndex?t[i.endIndex]?.timestamp:null,status:i.endIndex?"completed":"active"}),n.mainPM.totalEvents+=i.agentEvents.length;t.forEach((e,t)=>{const a=this.getInferredAgent(t);a&&"main_agent"===a.type&&(n.mainPM.ownEvents.push({eventIndex:t,event:e}),n.mainPM.totalEvents++)});const a=new Map;t.forEach((t,n)=>{const s=this.getInferredAgent(n);if(s&&"subagent"===s.type){let i=!0;for(const[t,a]of e)if(a.agentEvents.some(e=>e.eventIndex===n)){i=!1;break}if(i){const e=s.agentName;a.has(e)||a.set(e,[]),a.get(e).push({eventIndex:n,event:t,inference:s})}}});for(const[s,i]of a)n.impliedPM.delegations.push({id:`implied_${s}`,agentName:s,taskContext:"No explicit PM delegation",events:i,startTime:i[0].event.timestamp,endTime:i[i.length-1].event.timestamp,status:"completed"}),n.impliedPM.totalEvents+=i.length;return n}extractTaskContext(e){if(!e)return"Unknown task";const t=e.tool_parameters||e.data?.tool_parameters||{};return t.task||t.request||t.description||"Task delegation"}identifyOrphanSubagents(e){for(const[t,n]of this.state.subagentStartEvents)for(const a of n){const n=a.eventIndex,s=new Date(a.timestamp).getTime();let i=!1;for(let a=Math.max(0,n-20);a<n;a++){const n=e[a];if(!n)continue;const o=s-new Date(n.timestamp).getTime();if("Task"===n.tool_name&&o>=0&&o<1e4){const e=this.state.eventAgentMap.get(a);if(e&&e.agentName===t){i=!0;break}}}i||this.state.orphanSubagents.set(n,{agentName:t,timestamp:a.timestamp,sessionId:a.sessionId,event:a.event,groupingKey:null})}this.groupOrphanSubagents(5e3)}groupOrphanSubagents(e){const t=Array.from(this.state.orphanSubagents.values()).sort((e,t)=>new Date(e.timestamp)-new Date(t.timestamp));let n=null,a=null;for(const s of t){const t=new Date(s.timestamp).getTime();(!n||a&&t-a>e)&&(n=`implied_pm_${s.sessionId}_${t}`),s.groupingKey=n,a=t}}isOrphanSubagent(e){return this.state.orphanSubagents.has(e)}getOrphanContext(e){return this.state.orphanSubagents.get(e)||null}getOrphanGroups(){const e=new Map;for(const t of this.state.orphanSubagents.values()){const n=t.groupingKey;e.has(n)||e.set(n,[]),e.get(n).push(t)}return e}getUniqueAgentInstances(){const e=new Map;for(const[a,s]of this.state.pmDelegations){const t=s.agentName;e.has(t)||e.set(t,{id:`consolidated_${t}`,type:"consolidated_agent",agentName:t,delegations:[],pmCalls:[],allEvents:[],firstTimestamp:s.timestamp,lastTimestamp:s.timestamp,totalEventCount:s.agentEvents.length,delegationCount:1});const n=e.get(t);n.delegations.push({id:a,pmCall:s.pmCall,timestamp:s.timestamp,eventCount:s.agentEvents.length,startIndex:s.startIndex,endIndex:s.endIndex,events:s.agentEvents}),s.pmCall&&n.pmCalls.push(s.pmCall),n.allEvents=n.allEvents.concat(s.agentEvents),new Date(s.timestamp)<new Date(n.firstTimestamp)&&(n.firstTimestamp=s.timestamp),new Date(s.timestamp)>new Date(n.lastTimestamp)&&(n.lastTimestamp=s.timestamp),n.totalEventCount+=s.agentEvents.length,n.delegationCount++}const t=this.eventViewer.events;for(let a=0;a<t.length;a++){const n=this.getInferredAgent(a);n&&"subagent"===n.type&&!e.has(n.agentName)&&e.set(n.agentName,{id:`consolidated_${n.agentName}`,type:"consolidated_agent",agentName:n.agentName,delegations:[{id:`implied_pm_${n.agentName}_${a}`,pmCall:null,timestamp:t[a].timestamp,eventCount:1,startIndex:a,endIndex:null,events:[{eventIndex:a,event:t[a],inference:n}]}],pmCalls:[],allEvents:[{eventIndex:a,event:t[a],inference:n}],firstTimestamp:t[a].timestamp,lastTimestamp:t[a].timestamp,totalEventCount:1,delegationCount:1,isImplied:!0})}const n=Array.from(e.values()).sort((e,t)=>new Date(e.firstTimestamp)-new Date(t.firstTimestamp));return console.log("Consolidated unique agents:",{total_unique_agents:n.length,agents:n.map(e=>({name:e.agentName,delegations:e.delegationCount,totalEvents:e.totalEventCount}))}),n}}export{e as A};
2
2
  //# sourceMappingURL=agent-inference.js.map