claude-mpm 4.1.7__py3-none-any.whl โ†’ 4.1.10__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 (109) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +26 -1
  3. claude_mpm/agents/OUTPUT_STYLE.md +73 -0
  4. claude_mpm/agents/agents_metadata.py +57 -0
  5. claude_mpm/agents/templates/.claude-mpm/memories/README.md +17 -0
  6. claude_mpm/agents/templates/.claude-mpm/memories/engineer_memories.md +3 -0
  7. claude_mpm/agents/templates/agent-manager.json +263 -17
  8. claude_mpm/agents/templates/agent-manager.md +248 -10
  9. claude_mpm/agents/templates/agentic_coder_optimizer.json +222 -0
  10. claude_mpm/agents/templates/code_analyzer.json +18 -8
  11. claude_mpm/agents/templates/engineer.json +1 -1
  12. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250826_014258_728.md +39 -0
  13. claude_mpm/agents/templates/qa.json +1 -1
  14. claude_mpm/agents/templates/research.json +1 -1
  15. claude_mpm/cli/__init__.py +4 -0
  16. claude_mpm/cli/commands/__init__.py +6 -0
  17. claude_mpm/cli/commands/analyze.py +547 -0
  18. claude_mpm/cli/commands/analyze_code.py +524 -0
  19. claude_mpm/cli/commands/configure.py +223 -25
  20. claude_mpm/cli/commands/configure_tui.py +65 -61
  21. claude_mpm/cli/commands/debug.py +1387 -0
  22. claude_mpm/cli/parsers/analyze_code_parser.py +170 -0
  23. claude_mpm/cli/parsers/analyze_parser.py +135 -0
  24. claude_mpm/cli/parsers/base_parser.py +29 -0
  25. claude_mpm/cli/parsers/configure_parser.py +23 -0
  26. claude_mpm/cli/parsers/debug_parser.py +319 -0
  27. claude_mpm/config/socketio_config.py +21 -21
  28. claude_mpm/constants.py +3 -1
  29. claude_mpm/core/framework_loader.py +148 -6
  30. claude_mpm/core/log_manager.py +16 -13
  31. claude_mpm/core/logger.py +1 -1
  32. claude_mpm/core/unified_agent_registry.py +1 -1
  33. claude_mpm/dashboard/.claude-mpm/socketio-instances.json +1 -0
  34. claude_mpm/dashboard/analysis_runner.py +428 -0
  35. claude_mpm/dashboard/static/built/components/activity-tree.js +2 -0
  36. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  37. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  38. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  39. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  40. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  41. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  42. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  43. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  44. claude_mpm/dashboard/static/css/activity.css +549 -0
  45. claude_mpm/dashboard/static/css/code-tree.css +846 -0
  46. claude_mpm/dashboard/static/css/dashboard.css +245 -0
  47. claude_mpm/dashboard/static/dist/components/activity-tree.js +2 -0
  48. claude_mpm/dashboard/static/dist/components/code-tree.js +2 -0
  49. claude_mpm/dashboard/static/dist/components/code-viewer.js +2 -0
  50. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  51. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  52. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  53. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  54. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  55. claude_mpm/dashboard/static/js/components/activity-tree.js +1139 -0
  56. claude_mpm/dashboard/static/js/components/code-tree.js +1357 -0
  57. claude_mpm/dashboard/static/js/components/code-viewer.js +480 -0
  58. claude_mpm/dashboard/static/js/components/event-viewer.js +11 -0
  59. claude_mpm/dashboard/static/js/components/session-manager.js +40 -4
  60. claude_mpm/dashboard/static/js/components/socket-manager.js +12 -0
  61. claude_mpm/dashboard/static/js/components/ui-state-manager.js +4 -0
  62. claude_mpm/dashboard/static/js/components/working-directory.js +17 -1
  63. claude_mpm/dashboard/static/js/dashboard.js +39 -0
  64. claude_mpm/dashboard/static/js/socket-client.js +414 -20
  65. claude_mpm/dashboard/templates/index.html +184 -4
  66. claude_mpm/hooks/claude_hooks/hook_handler.py +182 -5
  67. claude_mpm/hooks/claude_hooks/installer.py +728 -0
  68. claude_mpm/scripts/claude-hook-handler.sh +161 -0
  69. claude_mpm/scripts/socketio_daemon.py +121 -8
  70. claude_mpm/services/agents/deployment/agent_config_provider.py +127 -27
  71. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +2 -2
  72. claude_mpm/services/agents/deployment/agent_record_service.py +1 -2
  73. claude_mpm/services/agents/memory/memory_format_service.py +1 -5
  74. claude_mpm/services/cli/agent_cleanup_service.py +1 -2
  75. claude_mpm/services/cli/agent_dependency_service.py +1 -1
  76. claude_mpm/services/cli/agent_validation_service.py +3 -4
  77. claude_mpm/services/cli/dashboard_launcher.py +2 -3
  78. claude_mpm/services/cli/startup_checker.py +0 -10
  79. claude_mpm/services/core/cache_manager.py +1 -2
  80. claude_mpm/services/core/path_resolver.py +1 -4
  81. claude_mpm/services/core/service_container.py +2 -2
  82. claude_mpm/services/diagnostics/checks/instructions_check.py +2 -5
  83. claude_mpm/services/event_bus/direct_relay.py +98 -20
  84. claude_mpm/services/infrastructure/monitoring/__init__.py +11 -11
  85. claude_mpm/services/infrastructure/monitoring.py +11 -11
  86. claude_mpm/services/project/architecture_analyzer.py +1 -1
  87. claude_mpm/services/project/dependency_analyzer.py +4 -4
  88. claude_mpm/services/project/language_analyzer.py +3 -3
  89. claude_mpm/services/project/metrics_collector.py +3 -6
  90. claude_mpm/services/socketio/handlers/__init__.py +2 -0
  91. claude_mpm/services/socketio/handlers/code_analysis.py +170 -0
  92. claude_mpm/services/socketio/handlers/registry.py +2 -0
  93. claude_mpm/services/socketio/server/connection_manager.py +95 -65
  94. claude_mpm/services/socketio/server/core.py +125 -17
  95. claude_mpm/services/socketio/server/main.py +44 -5
  96. claude_mpm/services/visualization/__init__.py +19 -0
  97. claude_mpm/services/visualization/mermaid_generator.py +938 -0
  98. claude_mpm/tools/__main__.py +208 -0
  99. claude_mpm/tools/code_tree_analyzer.py +778 -0
  100. claude_mpm/tools/code_tree_builder.py +632 -0
  101. claude_mpm/tools/code_tree_events.py +318 -0
  102. claude_mpm/tools/socketio_debug.py +671 -0
  103. {claude_mpm-4.1.7.dist-info โ†’ claude_mpm-4.1.10.dist-info}/METADATA +1 -1
  104. {claude_mpm-4.1.7.dist-info โ†’ claude_mpm-4.1.10.dist-info}/RECORD +108 -77
  105. claude_mpm/agents/schema/agent_schema.json +0 -314
  106. {claude_mpm-4.1.7.dist-info โ†’ claude_mpm-4.1.10.dist-info}/WHEEL +0 -0
  107. {claude_mpm-4.1.7.dist-info โ†’ claude_mpm-4.1.10.dist-info}/entry_points.txt +0 -0
  108. {claude_mpm-4.1.7.dist-info โ†’ claude_mpm-4.1.10.dist-info}/licenses/LICENSE +0 -0
  109. {claude_mpm-4.1.7.dist-info โ†’ claude_mpm-4.1.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,428 @@
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
+
243
+ # Start subprocess
244
+ self.current_process = subprocess.Popen(
245
+ cmd,
246
+ stdout=subprocess.PIPE,
247
+ stderr=subprocess.PIPE,
248
+ text=True,
249
+ bufsize=1,
250
+ universal_newlines=True,
251
+ env=self._get_subprocess_env(),
252
+ )
253
+
254
+ # Process output
255
+ self._process_output(request)
256
+
257
+ # Wait for completion
258
+ return_code = self.current_process.wait()
259
+
260
+ if self.cancel_event.is_set():
261
+ # Analysis was cancelled
262
+ pass # Event already emitted in cancel_current
263
+ elif return_code == 0:
264
+ # Success
265
+ self.stats["analyses_completed"] += 1
266
+ self._emit_event(
267
+ "code:analysis:complete",
268
+ {
269
+ "request_id": request.request_id,
270
+ "path": request.path,
271
+ "stats": {
272
+ "total_files": self.stats["total_files"],
273
+ "total_nodes": self.stats["total_nodes"],
274
+ },
275
+ },
276
+ )
277
+ else:
278
+ # Failure
279
+ self.stats["analyses_failed"] += 1
280
+ stderr = (
281
+ self.current_process.stderr.read()
282
+ if self.current_process.stderr
283
+ else ""
284
+ )
285
+ self._emit_error(
286
+ request.request_id,
287
+ f"Analysis failed with code {return_code}: {stderr}",
288
+ )
289
+
290
+ except Exception as e:
291
+ self.logger.error(f"Error processing request {request.request_id}: {e}")
292
+ self.stats["analyses_failed"] += 1
293
+ self._emit_error(request.request_id, str(e))
294
+
295
+ finally:
296
+ self.current_process = None
297
+ self.current_request = None
298
+
299
+ def _build_command(self, request: AnalysisRequest) -> List[str]:
300
+ """Build the subprocess command for analysis.
301
+
302
+ Args:
303
+ request: The analysis request
304
+
305
+ Returns:
306
+ Command list for subprocess.Popen
307
+ """
308
+ # Get Python executable
309
+ python_exe = sys.executable
310
+
311
+ # Build command - use the tools module as a runnable module
312
+ cmd = [
313
+ python_exe,
314
+ "-m",
315
+ "claude_mpm.tools",
316
+ "--path",
317
+ request.path,
318
+ "--emit-events",
319
+ "--output-format",
320
+ "json-stream",
321
+ ]
322
+
323
+ # Add optional parameters
324
+ if request.languages:
325
+ cmd.extend(["--languages", ",".join(request.languages)])
326
+
327
+ if request.max_depth:
328
+ cmd.extend(["--max-depth", str(request.max_depth)])
329
+
330
+ if request.ignore_patterns:
331
+ for pattern in request.ignore_patterns:
332
+ cmd.extend(["--ignore", pattern])
333
+
334
+ return cmd
335
+
336
+ def _get_subprocess_env(self) -> Dict[str, str]:
337
+ """Get environment variables for subprocess.
338
+
339
+ Returns:
340
+ Environment dictionary for subprocess
341
+ """
342
+ env = os.environ.copy()
343
+
344
+ # Ensure Socket.IO URL is set for event emission
345
+ env["SOCKETIO_URL"] = f"http://localhost:{self.server.port}"
346
+
347
+ # Set Python path to include our modules
348
+ python_path = env.get("PYTHONPATH", "")
349
+ src_path = str(Path(__file__).parent.parent.parent)
350
+ if src_path not in python_path:
351
+ env["PYTHONPATH"] = f"{src_path}:{python_path}" if python_path else src_path
352
+
353
+ return env
354
+
355
+ def _process_output(self, request: AnalysisRequest):
356
+ """Process subprocess output and emit events.
357
+
358
+ Args:
359
+ request: The current analysis request
360
+ """
361
+ if not self.current_process:
362
+ return
363
+
364
+ # Read output line by line
365
+ for line in iter(self.current_process.stdout.readline, ""):
366
+ if self.cancel_event.is_set():
367
+ break
368
+
369
+ line = line.strip()
370
+ if not line:
371
+ continue
372
+
373
+ try:
374
+ # Parse JSON event
375
+ event = json.loads(line)
376
+
377
+ # Route event to appropriate handler
378
+ event_type = event.get("type")
379
+ event_data = event.get("data", {})
380
+
381
+ # Add request ID to event data
382
+ event_data["request_id"] = request.request_id
383
+
384
+ # Update statistics based on event type
385
+ if event_type == "code:file:complete":
386
+ self.stats["total_files"] += 1
387
+ elif event_type == "code:node:found":
388
+ self.stats["total_nodes"] += 1
389
+
390
+ # Emit to Socket.IO clients
391
+ self._emit_event(event_type, event_data)
392
+
393
+ except json.JSONDecodeError:
394
+ # Not JSON, treat as log message
395
+ self.logger.debug(f"Analyzer output: {line}")
396
+ except Exception as e:
397
+ self.logger.warning(f"Error processing analyzer output: {e}")
398
+
399
+ def _emit_event(self, event_type: str, data: Dict[str, Any]):
400
+ """Emit an event to Socket.IO clients.
401
+
402
+ Args:
403
+ event_type: Type of event
404
+ data: Event data
405
+ """
406
+ if self.server:
407
+ # Add timestamp if not present
408
+ if "timestamp" not in data:
409
+ data["timestamp"] = datetime.utcnow().isoformat()
410
+
411
+ # Broadcast to all clients
412
+ self.server.broadcast_event(event_type, data)
413
+
414
+ def _emit_error(self, request_id: str, message: str):
415
+ """Emit an error event.
416
+
417
+ Args:
418
+ request_id: Request that caused the error
419
+ message: Error message
420
+ """
421
+ self._emit_event(
422
+ "code:analysis:error",
423
+ {
424
+ "request_id": request_id,
425
+ "message": message,
426
+ "timestamp": datetime.utcnow().isoformat(),
427
+ },
428
+ )
@@ -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"),!this.container)return void console.error("Activity tree container (#activity-tree) not found in DOM");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.setupControls(),this.initializeTreeData(),this.subscribeToEvents(),void(this.initialized=!0);this.setupControls(),this.createVisualization(),this.svg&&this.treeGroup?(this.initializeTreeData(),this.update(this.root),this.subscribeToEvents(),this.initialized=!0,console.log("Activity tree initialization complete")):console.error("Failed to create D3 visualization elements")}else console.error("Activity tab panel (#activity-tab) not found in DOM")}renderWhenVisible(){if(console.log("ActivityTree.renderWhenVisible() called"),!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.update(this.root)),this.root&&this.svg&&(console.log("Updating tree with current data..."),this.update(this.root))}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 n=document.getElementById("time-range");n&&n.addEventListener("change",t=>{this.timeRange=t.target.value,this.filterEventsByTime()});const o=document.getElementById("activity-search");o&&o.addEventListener("input",t=>{this.searchTerm=t.target.value.toLowerCase(),this.highlightSearchResults()})}createVisualization(){if("undefined"==typeof d3)return void console.error("D3.js is not loaded! Cannot create activity tree visualization.");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).select("svg").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]),this.tooltip=d3.select("body").append("div").attr("class","activity-tooltip").style("opacity",0)}initializeTreeData(){this.treeData={name:"PM",type:"pm",icon:"๐ŸŽฏ",children:[],_children:null},this.root=d3.hierarchy(this.treeData),this.root.x0=this.height/2,this.root.y0=0}subscribeToEvents(){if(!window.socketClient)return console.warn("Socket client not available for activity tree"),void setTimeout(()=>this.subscribeToEvents(),1e3);if(window.socketClient.onEventUpdate(t=>{const e=t.length-this.events.length;if(e>0){const i=t.slice(this.events.length);console.log("ActivityTree: Received new events:",i.map(t=>({type:t.type,subtype:t.subtype,hook_event_name:t.hook_event_name,tool_name:t.tool_name,originalEventName:t.originalEventName})));const n=i.filter(t=>t.hook_event_name).length,o=i.filter(t=>"hook"===t.type||"todo"===t.type||"subagent"===t.type).length;console.log(`ActivityTree: ${n}/${e} new events have hook_event_name, ${o} are hook/todo/subagent type`),i.forEach(t=>{t.hook_event_name?this.processEvent(t):"hook"!==t.type&&"todo"!==t.type&&"subagent"!==t.type||console.warn("ActivityTree: Missing hook_event_name for relevant event:",{type:t.type,subtype:t.subtype,tool_name:t.tool_name})}),this.events=[...t]}}),window.socketClient&&window.socketClient.events){console.log(`Processing ${window.socketClient.events.length} existing events for activity tree`);const t=window.socketClient.events.filter(t=>t.hook_event_name).length,e=window.socketClient.events.filter(t=>"hook"===t.type||"todo"===t.type||"subagent"===t.type).length;console.log(`ActivityTree: ${t}/${window.socketClient.events.length} existing events have hook_event_name, ${e} are hook/todo/subagent type`);const i=window.socketClient.events.slice(0,5);console.log("ActivityTree: Sample of existing events:",i.map(t=>({type:t.type,subtype:t.subtype,hook_event_name:t.hook_event_name,tool_name:t.tool_name,originalEventName:t.originalEventName}))),window.socketClient.events.forEach(t=>{t.hook_event_name&&this.processEvent(t)}),this.events=[...window.socketClient.events]}else window.eventViewer&&window.eventViewer.events?(console.log(`Processing ${window.eventViewer.events.length} existing events for activity tree (from eventViewer)`),window.eventViewer.events.forEach(t=>{t.hook_event_name&&this.processEvent(t)}),this.events=[...window.eventViewer.events]):(console.log("No existing events found for activity tree"),this.events=[])}processEvent(t){if(!t||!t.hook_event_name)return void console.log("ActivityTree: Ignoring invalid event",t);console.log(`ActivityTree: Processing event: ${t.hook_event_name}`,t);const e=t.hook_event_name,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){const e=t.todos||t.data?.todos||[];if(0===e.length)return;const i=e.find(t=>"in_progress"===t.status);if(!i)return;const n={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.data.children||(this.root.data.children=[]),this.root.data.children.push(n),this.todoWriteStack.push({node:n,content:i.content}),this.update(this.root)}processSubagentStart(t){const e=t.agent_name||t.data?.agent_name||t.data?.agent_type||"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 n=null;if(this.todoWriteStack.length>0){const t=this.todoWriteStack[this.todoWriteStack.length-1];t.content&&t.content.toLowerCase().includes(e.toLowerCase())&&(n=t.node)}n||(n=this.root.data),n.children||(n.children=[]),n.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||"unknown",i=this.getToolIcon(e),n=t.tool_parameters||t.data?.tool_parameters||t.data?.tool_params||{},o={name:e,type:"tool",icon:i,timestamp:t.timestamp,status:"in_progress",children:[],_children:null,eventId:t.id};"Read"===e&&n.file_path?o.children.push({name:n.file_path,type:"file",icon:"๐Ÿ“„",timestamp:t.timestamp}):"Edit"===e&&n.file_path?o.children.push({name:n.file_path,type:"file",icon:"โœ๏ธ",timestamp:t.timestamp}):"Write"===e&&n.file_path?o.children.push({name:n.file_path,type:"file",icon:"๐Ÿ’พ",timestamp:t.timestamp}):"Bash"===e&&n.command?o.children.push({name:n.command.substring(0,50)+(n.command.length>50?"...":""),type:"command",icon:"โšก",timestamp:t.timestamp}):"WebFetch"===e&&n.url&&o.children.push({name:n.url,type:"url",icon:"๐ŸŒ",timestamp:t.timestamp});let s=this.activeAgent||this.root.data;s.children||(s.children=[]),s.children.push(o),this.update(this.root)}updateToolStatus(t,e){const i=n=>{if(n.eventId===t.id)return n.status=e,!0;if(n.children)for(let t of n.children)if(i(t))return!0;if(n._children)for(let t of n._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){const e=this.treeLayout(this.root),i=e.descendants(),n=e.links();i.forEach(t=>{t.y=180*t.depth});const o=this.treeGroup.selectAll("g.node").data(i,t=>t.id||(t.id=++this.nodeId)),s=o.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 a=s.merge(o);a.transition().duration(this.duration).attr("transform",t=>`translate(${t.y},${t.x})`),a.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}),a.select("text.node-label").style("fill-opacity",1);const r=o.exit().transition().duration(this.duration).attr("transform",e=>`translate(${t.y},${t.x})`).remove();r.select("circle").attr("r",1e-6),r.select("text").style("fill-opacity",1e-6);const l=this.treeGroup.selectAll("path.link").data(n,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(){const t=this.countNodes(this.root),e=this.countActiveNodes(this.root.data),i=this.getTreeDepth(this.root);document.getElementById("node-count").textContent=t,document.getElementById("active-count").textContent=e,document.getElementById("tree-depth").textContent=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 n=document.getElementById("activity-breadcrumb");n&&(n.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),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(),150))})}),document.addEventListener("tabChanged",t=>{t.detail&&"activity"===t.detail.newTab&&(console.log("Tab changed to activity, initializing tree..."),i(),e&&setTimeout(()=>e.renderWhenVisible(),150))});const n=document.querySelector(".tab-button.active");n&&"activity"===n.getAttribute("data-tab")&&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