claude-mpm 3.9.9__py3-none-any.whl โ 4.0.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/__init__.py +2 -2
- claude_mpm/__main__.py +3 -2
- claude_mpm/agents/__init__.py +85 -79
- claude_mpm/agents/agent_loader.py +464 -1003
- claude_mpm/agents/agent_loader_integration.py +45 -45
- claude_mpm/agents/agents_metadata.py +29 -30
- claude_mpm/agents/async_agent_loader.py +156 -138
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/base_agent_loader.py +179 -151
- claude_mpm/agents/frontmatter_validator.py +229 -130
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/system_agent_config.py +213 -147
- claude_mpm/agents/templates/__init__.py +13 -13
- claude_mpm/agents/templates/code_analyzer.json +2 -2
- claude_mpm/agents/templates/data_engineer.json +1 -1
- claude_mpm/agents/templates/documentation.json +23 -11
- claude_mpm/agents/templates/engineer.json +22 -6
- claude_mpm/agents/templates/memory_manager.json +155 -0
- claude_mpm/agents/templates/ops.json +2 -2
- claude_mpm/agents/templates/project_organizer.json +1 -1
- claude_mpm/agents/templates/qa.json +1 -1
- claude_mpm/agents/templates/refactoring_engineer.json +222 -0
- claude_mpm/agents/templates/research.json +20 -14
- claude_mpm/agents/templates/security.json +1 -1
- claude_mpm/agents/templates/ticketing.json +1 -1
- claude_mpm/agents/templates/version_control.json +1 -1
- claude_mpm/agents/templates/web_qa.json +3 -1
- claude_mpm/agents/templates/web_ui.json +2 -2
- claude_mpm/cli/__init__.py +90 -49
- claude_mpm/cli/__main__.py +3 -2
- claude_mpm/cli/commands/__init__.py +21 -18
- claude_mpm/cli/commands/agents.py +279 -247
- claude_mpm/cli/commands/aggregate.py +138 -157
- claude_mpm/cli/commands/cleanup.py +147 -147
- claude_mpm/cli/commands/config.py +93 -76
- claude_mpm/cli/commands/info.py +17 -16
- claude_mpm/cli/commands/mcp.py +143 -762
- claude_mpm/cli/commands/mcp_command_router.py +139 -0
- claude_mpm/cli/commands/mcp_config_commands.py +20 -0
- claude_mpm/cli/commands/mcp_install_commands.py +20 -0
- claude_mpm/cli/commands/mcp_server_commands.py +175 -0
- claude_mpm/cli/commands/mcp_tool_commands.py +34 -0
- claude_mpm/cli/commands/memory.py +239 -203
- claude_mpm/cli/commands/monitor.py +203 -81
- claude_mpm/cli/commands/run.py +380 -429
- claude_mpm/cli/commands/run_config_checker.py +160 -0
- claude_mpm/cli/commands/socketio_monitor.py +235 -0
- claude_mpm/cli/commands/tickets.py +305 -197
- claude_mpm/cli/parser.py +24 -1150
- claude_mpm/cli/parsers/__init__.py +29 -0
- claude_mpm/cli/parsers/agents_parser.py +136 -0
- claude_mpm/cli/parsers/base_parser.py +331 -0
- claude_mpm/cli/parsers/config_parser.py +85 -0
- claude_mpm/cli/parsers/mcp_parser.py +152 -0
- claude_mpm/cli/parsers/memory_parser.py +138 -0
- claude_mpm/cli/parsers/monitor_parser.py +104 -0
- claude_mpm/cli/parsers/run_parser.py +147 -0
- claude_mpm/cli/parsers/tickets_parser.py +203 -0
- claude_mpm/cli/ticket_cli.py +7 -3
- claude_mpm/cli/utils.py +55 -37
- claude_mpm/cli_module/__init__.py +6 -6
- claude_mpm/cli_module/args.py +188 -140
- claude_mpm/cli_module/commands.py +79 -70
- claude_mpm/cli_module/migration_example.py +38 -60
- claude_mpm/config/__init__.py +32 -25
- claude_mpm/config/agent_config.py +151 -119
- claude_mpm/config/experimental_features.py +217 -0
- claude_mpm/config/paths.py +94 -208
- claude_mpm/config/socketio_config.py +84 -73
- claude_mpm/constants.py +36 -18
- claude_mpm/core/__init__.py +9 -6
- claude_mpm/core/agent_name_normalizer.py +68 -71
- claude_mpm/core/agent_registry.py +372 -521
- claude_mpm/core/agent_session_manager.py +74 -63
- claude_mpm/core/base_service.py +116 -87
- claude_mpm/core/cache.py +119 -153
- claude_mpm/core/claude_runner.py +425 -1120
- claude_mpm/core/config.py +263 -168
- claude_mpm/core/config_aliases.py +69 -61
- claude_mpm/core/config_constants.py +292 -0
- claude_mpm/core/constants.py +57 -99
- claude_mpm/core/container.py +211 -178
- claude_mpm/core/exceptions.py +233 -89
- claude_mpm/core/factories.py +92 -54
- claude_mpm/core/framework_loader.py +378 -220
- claude_mpm/core/hook_manager.py +198 -83
- claude_mpm/core/hook_performance_config.py +136 -0
- claude_mpm/core/injectable_service.py +61 -55
- claude_mpm/core/interactive_session.py +165 -155
- claude_mpm/core/interfaces.py +221 -195
- claude_mpm/core/lazy.py +96 -96
- claude_mpm/core/logger.py +133 -107
- claude_mpm/core/logging_config.py +185 -157
- claude_mpm/core/minimal_framework_loader.py +20 -15
- claude_mpm/core/mixins.py +30 -29
- claude_mpm/core/oneshot_session.py +215 -181
- claude_mpm/core/optimized_agent_loader.py +134 -138
- claude_mpm/core/optimized_startup.py +159 -157
- claude_mpm/core/pm_hook_interceptor.py +85 -72
- claude_mpm/core/service_registry.py +103 -101
- claude_mpm/core/session_manager.py +97 -87
- claude_mpm/core/socketio_pool.py +212 -158
- claude_mpm/core/tool_access_control.py +58 -51
- claude_mpm/core/types.py +46 -24
- claude_mpm/core/typing_utils.py +166 -82
- claude_mpm/core/unified_agent_registry.py +721 -0
- claude_mpm/core/unified_config.py +550 -0
- claude_mpm/core/unified_paths.py +549 -0
- claude_mpm/dashboard/index.html +1 -1
- claude_mpm/dashboard/open_dashboard.py +51 -17
- claude_mpm/dashboard/static/css/dashboard.css +27 -8
- claude_mpm/dashboard/static/dist/components/agent-inference.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-processor.js +2 -0
- claude_mpm/dashboard/static/dist/components/event-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/export-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-library-loader.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/hud-visualizer.js +2 -0
- claude_mpm/dashboard/static/dist/components/module-viewer.js +2 -0
- claude_mpm/dashboard/static/dist/components/session-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/socket-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/ui-state-manager.js +2 -0
- claude_mpm/dashboard/static/dist/components/working-directory.js +2 -0
- claude_mpm/dashboard/static/dist/dashboard.js +2 -0
- claude_mpm/dashboard/static/dist/socket-client.js +2 -0
- claude_mpm/dashboard/static/js/components/agent-inference.js +80 -76
- claude_mpm/dashboard/static/js/components/event-processor.js +71 -67
- claude_mpm/dashboard/static/js/components/event-viewer.js +74 -70
- claude_mpm/dashboard/static/js/components/export-manager.js +31 -28
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +106 -92
- claude_mpm/dashboard/static/js/components/hud-library-loader.js +11 -11
- claude_mpm/dashboard/static/js/components/hud-manager.js +73 -73
- claude_mpm/dashboard/static/js/components/hud-visualizer.js +163 -163
- claude_mpm/dashboard/static/js/components/module-viewer.js +305 -233
- claude_mpm/dashboard/static/js/components/session-manager.js +32 -29
- claude_mpm/dashboard/static/js/components/socket-manager.js +27 -20
- claude_mpm/dashboard/static/js/components/ui-state-manager.js +21 -18
- claude_mpm/dashboard/static/js/components/working-directory.js +74 -71
- claude_mpm/dashboard/static/js/dashboard.js +178 -453
- claude_mpm/dashboard/static/js/extension-error-handler.js +164 -0
- claude_mpm/dashboard/static/js/socket-client.js +120 -54
- claude_mpm/dashboard/templates/index.html +40 -50
- claude_mpm/experimental/cli_enhancements.py +60 -58
- claude_mpm/generators/__init__.py +1 -1
- claude_mpm/generators/agent_profile_generator.py +75 -65
- claude_mpm/hooks/__init__.py +1 -1
- claude_mpm/hooks/base_hook.py +33 -28
- claude_mpm/hooks/claude_hooks/__init__.py +1 -1
- claude_mpm/hooks/claude_hooks/connection_pool.py +120 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +743 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +415 -1331
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +4 -4
- claude_mpm/hooks/claude_hooks/memory_integration.py +221 -0
- claude_mpm/hooks/claude_hooks/response_tracking.py +348 -0
- claude_mpm/hooks/claude_hooks/tool_analysis.py +230 -0
- claude_mpm/hooks/memory_integration_hook.py +140 -100
- claude_mpm/hooks/tool_call_interceptor.py +89 -76
- claude_mpm/hooks/validation_hooks.py +57 -49
- claude_mpm/init.py +145 -121
- claude_mpm/models/__init__.py +9 -9
- claude_mpm/models/agent_definition.py +33 -23
- claude_mpm/models/agent_session.py +228 -200
- claude_mpm/scripts/__init__.py +1 -1
- claude_mpm/scripts/socketio_daemon.py +192 -75
- claude_mpm/scripts/socketio_server_manager.py +328 -0
- claude_mpm/scripts/start_activity_logging.py +25 -22
- claude_mpm/services/__init__.py +68 -43
- claude_mpm/services/agent_capabilities_service.py +271 -0
- claude_mpm/services/agents/__init__.py +23 -32
- claude_mpm/services/agents/deployment/__init__.py +3 -3
- claude_mpm/services/agents/deployment/agent_config_provider.py +310 -0
- claude_mpm/services/agents/deployment/agent_configuration_manager.py +359 -0
- claude_mpm/services/agents/deployment/agent_definition_factory.py +84 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +415 -2113
- claude_mpm/services/agents/deployment/agent_discovery_service.py +387 -0
- claude_mpm/services/agents/deployment/agent_environment_manager.py +293 -0
- claude_mpm/services/agents/deployment/agent_filesystem_manager.py +387 -0
- claude_mpm/services/agents/deployment/agent_format_converter.py +453 -0
- claude_mpm/services/agents/deployment/agent_frontmatter_validator.py +161 -0
- claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +345 -495
- claude_mpm/services/agents/deployment/agent_metrics_collector.py +279 -0
- claude_mpm/services/agents/deployment/agent_restore_handler.py +88 -0
- claude_mpm/services/agents/deployment/agent_template_builder.py +406 -0
- claude_mpm/services/agents/deployment/agent_validator.py +352 -0
- claude_mpm/services/agents/deployment/agent_version_manager.py +313 -0
- claude_mpm/services/agents/deployment/agent_versioning.py +6 -9
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +79 -0
- claude_mpm/services/agents/deployment/async_agent_deployment.py +298 -234
- claude_mpm/services/agents/deployment/config/__init__.py +13 -0
- claude_mpm/services/agents/deployment/config/deployment_config.py +182 -0
- claude_mpm/services/agents/deployment/config/deployment_config_manager.py +200 -0
- claude_mpm/services/agents/deployment/deployment_config_loader.py +54 -0
- claude_mpm/services/agents/deployment/deployment_type_detector.py +124 -0
- claude_mpm/services/agents/deployment/facade/__init__.py +18 -0
- claude_mpm/services/agents/deployment/facade/async_deployment_executor.py +159 -0
- claude_mpm/services/agents/deployment/facade/deployment_executor.py +73 -0
- claude_mpm/services/agents/deployment/facade/deployment_facade.py +270 -0
- claude_mpm/services/agents/deployment/facade/sync_deployment_executor.py +178 -0
- claude_mpm/services/agents/deployment/interface_adapter.py +227 -0
- claude_mpm/services/agents/deployment/lifecycle_health_checker.py +85 -0
- claude_mpm/services/agents/deployment/lifecycle_performance_tracker.py +100 -0
- claude_mpm/services/agents/deployment/pipeline/__init__.py +32 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_builder.py +158 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_context.py +159 -0
- claude_mpm/services/agents/deployment/pipeline/pipeline_executor.py +169 -0
- claude_mpm/services/agents/deployment/pipeline/steps/__init__.py +19 -0
- claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +195 -0
- claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +119 -0
- claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +79 -0
- claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +90 -0
- claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +100 -0
- claude_mpm/services/agents/deployment/processors/__init__.py +15 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_context.py +98 -0
- claude_mpm/services/agents/deployment/processors/agent_deployment_result.py +235 -0
- claude_mpm/services/agents/deployment/processors/agent_processor.py +258 -0
- claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +318 -0
- claude_mpm/services/agents/deployment/results/__init__.py +13 -0
- claude_mpm/services/agents/deployment/results/deployment_metrics.py +200 -0
- claude_mpm/services/agents/deployment/results/deployment_result_builder.py +249 -0
- claude_mpm/services/agents/deployment/strategies/__init__.py +25 -0
- claude_mpm/services/agents/deployment/strategies/base_strategy.py +119 -0
- claude_mpm/services/agents/deployment/strategies/project_strategy.py +150 -0
- claude_mpm/services/agents/deployment/strategies/strategy_selector.py +117 -0
- claude_mpm/services/agents/deployment/strategies/system_strategy.py +116 -0
- claude_mpm/services/agents/deployment/strategies/user_strategy.py +137 -0
- claude_mpm/services/agents/deployment/system_instructions_deployer.py +108 -0
- claude_mpm/services/agents/deployment/validation/__init__.py +19 -0
- claude_mpm/services/agents/deployment/validation/agent_validator.py +323 -0
- claude_mpm/services/agents/deployment/validation/deployment_validator.py +238 -0
- claude_mpm/services/agents/deployment/validation/template_validator.py +299 -0
- claude_mpm/services/agents/deployment/validation/validation_result.py +226 -0
- claude_mpm/services/agents/loading/__init__.py +2 -2
- claude_mpm/services/agents/loading/agent_profile_loader.py +259 -229
- claude_mpm/services/agents/loading/base_agent_manager.py +90 -81
- claude_mpm/services/agents/loading/framework_agent_loader.py +154 -129
- claude_mpm/services/agents/management/__init__.py +2 -2
- claude_mpm/services/agents/management/agent_capabilities_generator.py +72 -58
- claude_mpm/services/agents/management/agent_management_service.py +209 -156
- claude_mpm/services/agents/memory/__init__.py +9 -6
- claude_mpm/services/agents/memory/agent_memory_manager.py +218 -1152
- claude_mpm/services/agents/memory/agent_persistence_service.py +20 -16
- claude_mpm/services/agents/memory/analyzer.py +430 -0
- claude_mpm/services/agents/memory/content_manager.py +376 -0
- claude_mpm/services/agents/memory/template_generator.py +468 -0
- claude_mpm/services/agents/registry/__init__.py +7 -10
- claude_mpm/services/agents/registry/deployed_agent_discovery.py +122 -97
- claude_mpm/services/agents/registry/modification_tracker.py +351 -285
- claude_mpm/services/async_session_logger.py +187 -153
- claude_mpm/services/claude_session_logger.py +87 -72
- claude_mpm/services/command_handler_service.py +217 -0
- claude_mpm/services/communication/__init__.py +3 -2
- claude_mpm/services/core/__init__.py +50 -97
- claude_mpm/services/core/base.py +60 -53
- claude_mpm/services/core/interfaces/__init__.py +188 -0
- claude_mpm/services/core/interfaces/agent.py +351 -0
- claude_mpm/services/core/interfaces/communication.py +343 -0
- claude_mpm/services/core/interfaces/infrastructure.py +413 -0
- claude_mpm/services/core/interfaces/service.py +434 -0
- claude_mpm/services/core/interfaces.py +19 -944
- claude_mpm/services/event_aggregator.py +208 -170
- claude_mpm/services/exceptions.py +387 -308
- claude_mpm/services/framework_claude_md_generator/__init__.py +75 -79
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +69 -60
- claude_mpm/services/framework_claude_md_generator/content_validator.py +65 -61
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +68 -49
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +34 -34
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +25 -22
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +10 -10
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +8 -7
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +6 -5
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +9 -8
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +4 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +5 -4
- claude_mpm/services/framework_claude_md_generator/section_manager.py +28 -27
- claude_mpm/services/framework_claude_md_generator/version_manager.py +30 -28
- claude_mpm/services/hook_service.py +106 -114
- claude_mpm/services/infrastructure/__init__.py +7 -5
- claude_mpm/services/infrastructure/context_preservation.py +571 -0
- claude_mpm/services/infrastructure/daemon_manager.py +279 -0
- claude_mpm/services/infrastructure/logging.py +83 -76
- claude_mpm/services/infrastructure/monitoring.py +547 -404
- claude_mpm/services/mcp_gateway/__init__.py +40 -23
- claude_mpm/services/mcp_gateway/config/__init__.py +2 -2
- claude_mpm/services/mcp_gateway/config/config_loader.py +61 -56
- claude_mpm/services/mcp_gateway/config/config_schema.py +50 -41
- claude_mpm/services/mcp_gateway/config/configuration.py +82 -75
- claude_mpm/services/mcp_gateway/core/__init__.py +14 -21
- claude_mpm/services/mcp_gateway/core/base.py +80 -67
- claude_mpm/services/mcp_gateway/core/exceptions.py +60 -46
- claude_mpm/services/mcp_gateway/core/interfaces.py +97 -93
- claude_mpm/services/mcp_gateway/main.py +307 -127
- claude_mpm/services/mcp_gateway/registry/__init__.py +1 -1
- claude_mpm/services/mcp_gateway/registry/service_registry.py +100 -101
- claude_mpm/services/mcp_gateway/registry/tool_registry.py +135 -126
- claude_mpm/services/mcp_gateway/server/__init__.py +4 -4
- claude_mpm/services/mcp_gateway/server/{mcp_server.py โ mcp_gateway.py} +149 -153
- claude_mpm/services/mcp_gateway/server/stdio_handler.py +105 -107
- claude_mpm/services/mcp_gateway/server/stdio_server.py +691 -0
- claude_mpm/services/mcp_gateway/tools/__init__.py +4 -2
- claude_mpm/services/mcp_gateway/tools/base_adapter.py +110 -121
- claude_mpm/services/mcp_gateway/tools/document_summarizer.py +283 -215
- claude_mpm/services/mcp_gateway/tools/hello_world.py +122 -120
- claude_mpm/services/mcp_gateway/tools/ticket_tools.py +652 -0
- claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +606 -0
- claude_mpm/services/memory/__init__.py +2 -2
- claude_mpm/services/memory/builder.py +451 -362
- claude_mpm/services/memory/cache/__init__.py +2 -2
- claude_mpm/services/memory/cache/shared_prompt_cache.py +232 -194
- claude_mpm/services/memory/cache/simple_cache.py +107 -93
- claude_mpm/services/memory/indexed_memory.py +195 -193
- claude_mpm/services/memory/optimizer.py +267 -234
- claude_mpm/services/memory/router.py +571 -263
- claude_mpm/services/memory_hook_service.py +237 -0
- claude_mpm/services/port_manager.py +223 -0
- claude_mpm/services/project/__init__.py +3 -3
- claude_mpm/services/project/analyzer.py +451 -305
- claude_mpm/services/project/registry.py +262 -240
- claude_mpm/services/recovery_manager.py +287 -231
- claude_mpm/services/response_tracker.py +87 -67
- claude_mpm/services/runner_configuration_service.py +587 -0
- claude_mpm/services/session_management_service.py +304 -0
- claude_mpm/services/socketio/__init__.py +4 -4
- claude_mpm/services/socketio/client_proxy.py +174 -0
- claude_mpm/services/socketio/handlers/__init__.py +3 -3
- claude_mpm/services/socketio/handlers/base.py +44 -30
- claude_mpm/services/socketio/handlers/connection.py +145 -65
- claude_mpm/services/socketio/handlers/file.py +123 -108
- claude_mpm/services/socketio/handlers/git.py +607 -373
- claude_mpm/services/socketio/handlers/hook.py +170 -0
- claude_mpm/services/socketio/handlers/memory.py +4 -4
- claude_mpm/services/socketio/handlers/project.py +4 -4
- claude_mpm/services/socketio/handlers/registry.py +53 -38
- claude_mpm/services/socketio/server/__init__.py +18 -0
- claude_mpm/services/socketio/server/broadcaster.py +252 -0
- claude_mpm/services/socketio/server/core.py +399 -0
- claude_mpm/services/socketio/server/main.py +323 -0
- claude_mpm/services/socketio_client_manager.py +160 -133
- claude_mpm/services/socketio_server.py +36 -1885
- claude_mpm/services/subprocess_launcher_service.py +316 -0
- claude_mpm/services/system_instructions_service.py +258 -0
- claude_mpm/services/ticket_manager.py +20 -534
- claude_mpm/services/utility_service.py +285 -0
- claude_mpm/services/version_control/__init__.py +18 -21
- claude_mpm/services/version_control/branch_strategy.py +20 -10
- claude_mpm/services/version_control/conflict_resolution.py +37 -13
- claude_mpm/services/version_control/git_operations.py +52 -21
- claude_mpm/services/version_control/semantic_versioning.py +92 -53
- claude_mpm/services/version_control/version_parser.py +145 -125
- claude_mpm/services/version_service.py +270 -0
- claude_mpm/storage/__init__.py +9 -0
- claude_mpm/storage/state_storage.py +552 -0
- claude_mpm/ticket_wrapper.py +2 -2
- claude_mpm/utils/__init__.py +2 -2
- claude_mpm/utils/agent_dependency_loader.py +453 -243
- claude_mpm/utils/config_manager.py +157 -118
- claude_mpm/utils/console.py +1 -1
- claude_mpm/utils/dependency_cache.py +102 -107
- claude_mpm/utils/dependency_manager.py +52 -47
- claude_mpm/utils/dependency_strategies.py +131 -96
- claude_mpm/utils/environment_context.py +110 -102
- claude_mpm/utils/error_handler.py +75 -55
- claude_mpm/utils/file_utils.py +80 -67
- claude_mpm/utils/framework_detection.py +12 -11
- claude_mpm/utils/import_migration_example.py +12 -60
- claude_mpm/utils/imports.py +48 -45
- claude_mpm/utils/path_operations.py +100 -93
- claude_mpm/utils/robust_installer.py +172 -164
- claude_mpm/utils/session_logging.py +30 -23
- claude_mpm/utils/subprocess_utils.py +99 -61
- claude_mpm/validation/__init__.py +1 -1
- claude_mpm/validation/agent_validator.py +151 -111
- claude_mpm/validation/frontmatter_validator.py +92 -71
- {claude_mpm-3.9.9.dist-info โ claude_mpm-4.0.3.dist-info}/METADATA +51 -2
- claude_mpm-4.0.3.dist-info/RECORD +402 -0
- {claude_mpm-3.9.9.dist-info โ claude_mpm-4.0.3.dist-info}/entry_points.txt +1 -0
- {claude_mpm-3.9.9.dist-info โ claude_mpm-4.0.3.dist-info}/licenses/LICENSE +1 -1
- claude_mpm/config/memory_guardian_config.py +0 -325
- claude_mpm/core/config_paths.py +0 -150
- claude_mpm/dashboard/static/js/dashboard-original.js +0 -4134
- claude_mpm/deployment_paths.py +0 -261
- claude_mpm/hooks/claude_hooks/hook_handler_fixed.py +0 -454
- claude_mpm/models/state_models.py +0 -433
- claude_mpm/services/agent/__init__.py +0 -24
- claude_mpm/services/agent/deployment.py +0 -2548
- claude_mpm/services/agent/management.py +0 -598
- claude_mpm/services/agent/registry.py +0 -813
- claude_mpm/services/agents/registry/agent_registry.py +0 -813
- claude_mpm/services/communication/socketio.py +0 -1935
- claude_mpm/services/communication/websocket.py +0 -479
- claude_mpm/services/framework_claude_md_generator.py +0 -624
- claude_mpm/services/health_monitor.py +0 -893
- claude_mpm/services/infrastructure/memory_guardian.py +0 -770
- claude_mpm/services/mcp_gateway/server/mcp_server_simple.py +0 -444
- claude_mpm/services/optimized_hook_service.py +0 -542
- claude_mpm/services/project_analyzer.py +0 -864
- claude_mpm/services/project_registry.py +0 -608
- claude_mpm/services/standalone_socketio_server.py +0 -1300
- claude_mpm/services/ticket_manager_di.py +0 -318
- claude_mpm/services/ticketing_service_original.py +0 -510
- claude_mpm/utils/paths.py +0 -395
- claude_mpm/utils/platform_memory.py +0 -524
- claude_mpm-3.9.9.dist-info/RECORD +0 -293
- {claude_mpm-3.9.9.dist-info โ claude_mpm-4.0.3.dist-info}/WHEEL +0 -0
- {claude_mpm-3.9.9.dist-info โ claude_mpm-4.0.3.dist-info}/top_level.txt +0 -0
| @@ -1,4134 +0,0 @@ | |
| 1 | 
            -
            /**
         | 
| 2 | 
            -
             * Main Dashboard Application
         | 
| 3 | 
            -
             * Coordinates all components and handles tab management
         | 
| 4 | 
            -
             */
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            class Dashboard {
         | 
| 7 | 
            -
                constructor() {
         | 
| 8 | 
            -
                    // Components
         | 
| 9 | 
            -
                    this.socketClient = null;
         | 
| 10 | 
            -
                    this.eventViewer = null;
         | 
| 11 | 
            -
                    this.moduleViewer = null;
         | 
| 12 | 
            -
                    this.sessionManager = null;
         | 
| 13 | 
            -
                    this.hudVisualizer = null;
         | 
| 14 | 
            -
                    
         | 
| 15 | 
            -
                    // State
         | 
| 16 | 
            -
                    this.currentTab = 'events';
         | 
| 17 | 
            -
                    this.autoScroll = true;
         | 
| 18 | 
            -
                    this.hudMode = false;
         | 
| 19 | 
            -
                    
         | 
| 20 | 
            -
                    // Working directory state - will be set properly during initialization
         | 
| 21 | 
            -
                    this.currentWorkingDir = null;
         | 
| 22 | 
            -
                    
         | 
| 23 | 
            -
                    // Selection state - tracks the currently selected card across all tabs
         | 
| 24 | 
            -
                    this.selectedCard = {
         | 
| 25 | 
            -
                        tab: null,        // which tab the selection is in
         | 
| 26 | 
            -
                        index: null,      // index of selected item in that tab
         | 
| 27 | 
            -
                        type: null,       // 'event', 'agent', 'tool', 'file'
         | 
| 28 | 
            -
                        data: null        // the actual data object
         | 
| 29 | 
            -
                    };
         | 
| 30 | 
            -
                    
         | 
| 31 | 
            -
                    // Navigation state for each tab
         | 
| 32 | 
            -
                    this.tabNavigation = {
         | 
| 33 | 
            -
                        events: { selectedIndex: -1, items: [] },
         | 
| 34 | 
            -
                        agents: { selectedIndex: -1, items: [] },
         | 
| 35 | 
            -
                        tools: { selectedIndex: -1, items: [] },
         | 
| 36 | 
            -
                        files: { selectedIndex: -1, items: [] }
         | 
| 37 | 
            -
                    };
         | 
| 38 | 
            -
                    
         | 
| 39 | 
            -
                    // File tracking for files tab
         | 
| 40 | 
            -
                    this.fileOperations = new Map(); // Map of file paths to operations
         | 
| 41 | 
            -
                    
         | 
| 42 | 
            -
                    // Tool call tracking for tools tab
         | 
| 43 | 
            -
                    this.toolCalls = new Map(); // Map of tool call keys to paired pre/post events
         | 
| 44 | 
            -
                    
         | 
| 45 | 
            -
                    // Agent events tracking for agents tab
         | 
| 46 | 
            -
                    this.agentEvents = []; // Array of filtered agent events
         | 
| 47 | 
            -
                    
         | 
| 48 | 
            -
                    this.init();
         | 
| 49 | 
            -
                }
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                /**
         | 
| 52 | 
            -
                 * Initialize the dashboard
         | 
| 53 | 
            -
                 */
         | 
| 54 | 
            -
                init() {
         | 
| 55 | 
            -
                    // Initialize components
         | 
| 56 | 
            -
                    this.initializeComponents();
         | 
| 57 | 
            -
                    this.setupEventHandlers();
         | 
| 58 | 
            -
                    this.setupTabNavigation();
         | 
| 59 | 
            -
                    this.setupUnifiedKeyboardNavigation();
         | 
| 60 | 
            -
                    this.initializeFromURL();
         | 
| 61 | 
            -
                    
         | 
| 62 | 
            -
                    // Initialize agent inference system
         | 
| 63 | 
            -
                    this.initializeAgentInference();
         | 
| 64 | 
            -
                    
         | 
| 65 | 
            -
                    // Initialize working directory for current session
         | 
| 66 | 
            -
                    this.initializeWorkingDirectory();
         | 
| 67 | 
            -
                    
         | 
| 68 | 
            -
                    // Watch for footer directory changes
         | 
| 69 | 
            -
                    this.watchFooterDirectory();
         | 
| 70 | 
            -
                    
         | 
| 71 | 
            -
                    // Initialize HUD button state
         | 
| 72 | 
            -
                    this.updateHUDButtonState();
         | 
| 73 | 
            -
                    
         | 
| 74 | 
            -
                    console.log('Claude MPM Dashboard initialized');
         | 
| 75 | 
            -
                }
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                /**
         | 
| 78 | 
            -
                 * Initialize agent inference system
         | 
| 79 | 
            -
                 * Based on docs/design/main-subagent-identification.md
         | 
| 80 | 
            -
                 */
         | 
| 81 | 
            -
                initializeAgentInference() {
         | 
| 82 | 
            -
                    // Agent inference state tracking
         | 
| 83 | 
            -
                    this.agentInference = {
         | 
| 84 | 
            -
                        // Track current subagent delegation context
         | 
| 85 | 
            -
                        currentDelegation: null,
         | 
| 86 | 
            -
                        // Map of session_id -> agent context
         | 
| 87 | 
            -
                        sessionAgents: new Map(),
         | 
| 88 | 
            -
                        // Map of event indices -> inferred agent
         | 
| 89 | 
            -
                        eventAgentMap: new Map()
         | 
| 90 | 
            -
                    };
         | 
| 91 | 
            -
                    
         | 
| 92 | 
            -
                    console.log('Agent inference system initialized');
         | 
| 93 | 
            -
                }
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                /**
         | 
| 96 | 
            -
                 * Infer agent context from event payload
         | 
| 97 | 
            -
                 * Based on production-ready detection from design document
         | 
| 98 | 
            -
                 * @param {Object} event - Event payload
         | 
| 99 | 
            -
                 * @returns {Object} - {type: 'main_agent'|'subagent', confidence: 'definitive'|'high'|'medium'|'default', agentName: string}
         | 
| 100 | 
            -
                 */
         | 
| 101 | 
            -
                inferAgentFromEvent(event) {
         | 
| 102 | 
            -
                    // Handle both direct properties and nested data properties
         | 
| 103 | 
            -
                    const data = event.data || {};
         | 
| 104 | 
            -
                    const sessionId = event.session_id || data.session_id || 'unknown';
         | 
| 105 | 
            -
                    const eventType = event.hook_event_name || data.hook_event_name || event.type || '';
         | 
| 106 | 
            -
                    const subtype = event.subtype || '';
         | 
| 107 | 
            -
                    const toolName = event.tool_name || data.tool_name || '';
         | 
| 108 | 
            -
                    
         | 
| 109 | 
            -
                    // Direct event detection (highest confidence) - from design doc
         | 
| 110 | 
            -
                    if (eventType === 'SubagentStop' || subtype === 'subagent_stop') {
         | 
| 111 | 
            -
                        const agentName = this.extractAgentNameFromEvent(event);
         | 
| 112 | 
            -
                        return {
         | 
| 113 | 
            -
                            type: 'subagent',
         | 
| 114 | 
            -
                            confidence: 'definitive',
         | 
| 115 | 
            -
                            agentName: agentName,
         | 
| 116 | 
            -
                            reason: 'SubagentStop event'
         | 
| 117 | 
            -
                        };
         | 
| 118 | 
            -
                    }
         | 
| 119 | 
            -
                    
         | 
| 120 | 
            -
                    if (eventType === 'Stop' || subtype === 'stop') {
         | 
| 121 | 
            -
                        return {
         | 
| 122 | 
            -
                            type: 'main_agent',
         | 
| 123 | 
            -
                            confidence: 'definitive',
         | 
| 124 | 
            -
                            agentName: 'PM',
         | 
| 125 | 
            -
                            reason: 'Stop event'
         | 
| 126 | 
            -
                        };
         | 
| 127 | 
            -
                    }
         | 
| 128 | 
            -
                    
         | 
| 129 | 
            -
                    // Tool-based detection (high confidence) - from design doc
         | 
| 130 | 
            -
                    if (toolName === 'Task') {
         | 
| 131 | 
            -
                        const agentName = this.extractSubagentTypeFromTask(event);
         | 
| 132 | 
            -
                        if (agentName) {
         | 
| 133 | 
            -
                            return {
         | 
| 134 | 
            -
                                type: 'subagent',
         | 
| 135 | 
            -
                                confidence: 'high',
         | 
| 136 | 
            -
                                agentName: agentName,
         | 
| 137 | 
            -
                                reason: 'Task tool with subagent_type'
         | 
| 138 | 
            -
                            };
         | 
| 139 | 
            -
                        }
         | 
| 140 | 
            -
                    }
         | 
| 141 | 
            -
                    
         | 
| 142 | 
            -
                    // Hook event pattern analysis (high confidence)
         | 
| 143 | 
            -
                    if (eventType === 'PreToolUse' && toolName === 'Task') {
         | 
| 144 | 
            -
                        const agentName = this.extractSubagentTypeFromTask(event);
         | 
| 145 | 
            -
                        if (agentName) {
         | 
| 146 | 
            -
                            return {
         | 
| 147 | 
            -
                                type: 'subagent',
         | 
| 148 | 
            -
                                confidence: 'high',
         | 
| 149 | 
            -
                                agentName: agentName,
         | 
| 150 | 
            -
                                reason: 'PreToolUse Task delegation'
         | 
| 151 | 
            -
                            };
         | 
| 152 | 
            -
                        }
         | 
| 153 | 
            -
                    }
         | 
| 154 | 
            -
                    
         | 
| 155 | 
            -
                    // Session pattern analysis (medium confidence) - from design doc
         | 
| 156 | 
            -
                    if (sessionId) {
         | 
| 157 | 
            -
                        const sessionLower = sessionId.toLowerCase();
         | 
| 158 | 
            -
                        if (['subagent', 'task', 'agent-'].some(pattern => sessionLower.includes(pattern))) {
         | 
| 159 | 
            -
                            return {
         | 
| 160 | 
            -
                                type: 'subagent',
         | 
| 161 | 
            -
                                confidence: 'medium',
         | 
| 162 | 
            -
                                agentName: 'Subagent',
         | 
| 163 | 
            -
                                reason: 'Session ID pattern'
         | 
| 164 | 
            -
                            };
         | 
| 165 | 
            -
                        }
         | 
| 166 | 
            -
                    }
         | 
| 167 | 
            -
                    
         | 
| 168 | 
            -
                    // Agent type field analysis
         | 
| 169 | 
            -
                    const agentType = event.agent_type || event.data?.agent_type;
         | 
| 170 | 
            -
                    const subagentType = event.subagent_type || event.data?.subagent_type;
         | 
| 171 | 
            -
                    
         | 
| 172 | 
            -
                    if (subagentType && subagentType !== 'unknown') {
         | 
| 173 | 
            -
                        return {
         | 
| 174 | 
            -
                            type: 'subagent',
         | 
| 175 | 
            -
                            confidence: 'high',
         | 
| 176 | 
            -
                            agentName: subagentType,
         | 
| 177 | 
            -
                            reason: 'subagent_type field'
         | 
| 178 | 
            -
                        };
         | 
| 179 | 
            -
                    }
         | 
| 180 | 
            -
                    
         | 
| 181 | 
            -
                    if (agentType && agentType !== 'unknown' && agentType !== 'main') {
         | 
| 182 | 
            -
                        return {
         | 
| 183 | 
            -
                            type: 'subagent',
         | 
| 184 | 
            -
                            confidence: 'medium',
         | 
| 185 | 
            -
                            agentName: agentType,
         | 
| 186 | 
            -
                            reason: 'agent_type field'
         | 
| 187 | 
            -
                        };
         | 
| 188 | 
            -
                    }
         | 
| 189 | 
            -
                    
         | 
| 190 | 
            -
                    // Default to main agent (from design doc)
         | 
| 191 | 
            -
                    return {
         | 
| 192 | 
            -
                        type: 'main_agent',
         | 
| 193 | 
            -
                        confidence: 'default',
         | 
| 194 | 
            -
                        agentName: 'PM',
         | 
| 195 | 
            -
                        reason: 'default classification'
         | 
| 196 | 
            -
                    };
         | 
| 197 | 
            -
                }
         | 
| 198 | 
            -
             | 
| 199 | 
            -
                /**
         | 
| 200 | 
            -
                 * Extract subagent type from Task tool parameters
         | 
| 201 | 
            -
                 * @param {Object} event - Event with Task tool
         | 
| 202 | 
            -
                 * @returns {string|null} - Subagent type or null
         | 
| 203 | 
            -
                 */
         | 
| 204 | 
            -
                extractSubagentTypeFromTask(event) {
         | 
| 205 | 
            -
                    // Check tool_parameters directly
         | 
| 206 | 
            -
                    if (event.tool_parameters?.subagent_type) {
         | 
| 207 | 
            -
                        return event.tool_parameters.subagent_type;
         | 
| 208 | 
            -
                    }
         | 
| 209 | 
            -
                    
         | 
| 210 | 
            -
                    // Check nested in data.tool_parameters (hook events)
         | 
| 211 | 
            -
                    if (event.data?.tool_parameters?.subagent_type) {
         | 
| 212 | 
            -
                        return event.data.tool_parameters.subagent_type;
         | 
| 213 | 
            -
                    }
         | 
| 214 | 
            -
                    
         | 
| 215 | 
            -
                    // Check delegation_details (new structure)
         | 
| 216 | 
            -
                    if (event.data?.delegation_details?.agent_type) {
         | 
| 217 | 
            -
                        return event.data.delegation_details.agent_type;
         | 
| 218 | 
            -
                    }
         | 
| 219 | 
            -
                    
         | 
| 220 | 
            -
                    // Check tool_input fallback
         | 
| 221 | 
            -
                    if (event.tool_input?.subagent_type) {
         | 
| 222 | 
            -
                        return event.tool_input.subagent_type;
         | 
| 223 | 
            -
                    }
         | 
| 224 | 
            -
                    
         | 
| 225 | 
            -
                    return null;
         | 
| 226 | 
            -
                }
         | 
| 227 | 
            -
             | 
| 228 | 
            -
                /**
         | 
| 229 | 
            -
                 * Extract agent name from any event
         | 
| 230 | 
            -
                 * @param {Object} event - Event payload
         | 
| 231 | 
            -
                 * @returns {string} - Agent name
         | 
| 232 | 
            -
                 */
         | 
| 233 | 
            -
                extractAgentNameFromEvent(event) {
         | 
| 234 | 
            -
                    // Priority order based on reliability from design doc
         | 
| 235 | 
            -
                    const data = event.data || {};
         | 
| 236 | 
            -
                    
         | 
| 237 | 
            -
                    // 1. Task tool subagent_type (highest priority)
         | 
| 238 | 
            -
                    if (event.tool_name === 'Task' || data.tool_name === 'Task') {
         | 
| 239 | 
            -
                        const taskAgent = this.extractSubagentTypeFromTask(event);
         | 
| 240 | 
            -
                        if (taskAgent) return taskAgent;
         | 
| 241 | 
            -
                    }
         | 
| 242 | 
            -
                    
         | 
| 243 | 
            -
                    // 2. Direct subagent_type field
         | 
| 244 | 
            -
                    if (event.subagent_type && event.subagent_type !== 'unknown') {
         | 
| 245 | 
            -
                        return event.subagent_type;
         | 
| 246 | 
            -
                    }
         | 
| 247 | 
            -
                    if (data.subagent_type && data.subagent_type !== 'unknown') {
         | 
| 248 | 
            -
                        return data.subagent_type;
         | 
| 249 | 
            -
                    }
         | 
| 250 | 
            -
                    
         | 
| 251 | 
            -
                    // 3. Agent type fields (but not 'main' or 'unknown')
         | 
| 252 | 
            -
                    if (event.agent_type && !['main', 'unknown'].includes(event.agent_type)) {
         | 
| 253 | 
            -
                        return event.agent_type;
         | 
| 254 | 
            -
                    }
         | 
| 255 | 
            -
                    if (data.agent_type && !['main', 'unknown'].includes(data.agent_type)) {
         | 
| 256 | 
            -
                        return event.agent_type;
         | 
| 257 | 
            -
                    }
         | 
| 258 | 
            -
                    
         | 
| 259 | 
            -
                    if (event.data?.agent_type && !['main', 'unknown'].includes(event.data.agent_type)) {
         | 
| 260 | 
            -
                        return event.data.agent_type;
         | 
| 261 | 
            -
                    }
         | 
| 262 | 
            -
                    
         | 
| 263 | 
            -
                    // 5. Other fallbacks
         | 
| 264 | 
            -
                    if (event.agent && event.agent !== 'unknown') {
         | 
| 265 | 
            -
                        return event.agent;
         | 
| 266 | 
            -
                    }
         | 
| 267 | 
            -
                    
         | 
| 268 | 
            -
                    if (event.name && event.name !== 'unknown') {
         | 
| 269 | 
            -
                        return event.name;
         | 
| 270 | 
            -
                    }
         | 
| 271 | 
            -
                    
         | 
| 272 | 
            -
                    // Default fallback
         | 
| 273 | 
            -
                    return 'Unknown';
         | 
| 274 | 
            -
                }
         | 
| 275 | 
            -
             | 
| 276 | 
            -
                /**
         | 
| 277 | 
            -
                 * Process all events and build agent inference context
         | 
| 278 | 
            -
                 * This tracks delegation boundaries and agent context throughout the session
         | 
| 279 | 
            -
                 */
         | 
| 280 | 
            -
                processAgentInference() {
         | 
| 281 | 
            -
                    const events = this.eventViewer.events;
         | 
| 282 | 
            -
                    
         | 
| 283 | 
            -
                    // Reset inference state
         | 
| 284 | 
            -
                    this.agentInference.currentDelegation = null;
         | 
| 285 | 
            -
                    this.agentInference.sessionAgents.clear();
         | 
| 286 | 
            -
                    this.agentInference.eventAgentMap.clear();
         | 
| 287 | 
            -
                    
         | 
| 288 | 
            -
                    console.log('Processing agent inference for', events.length, 'events');
         | 
| 289 | 
            -
                    
         | 
| 290 | 
            -
                    // Process events chronologically to track delegation context
         | 
| 291 | 
            -
                    events.forEach((event, index) => {
         | 
| 292 | 
            -
                        const inference = this.inferAgentFromEvent(event);
         | 
| 293 | 
            -
                        const sessionId = event.session_id || 'default';
         | 
| 294 | 
            -
                        
         | 
| 295 | 
            -
                        // Track delegation boundaries
         | 
| 296 | 
            -
                        if (event.tool_name === 'Task' && inference.type === 'subagent') {
         | 
| 297 | 
            -
                            // Start of subagent delegation
         | 
| 298 | 
            -
                            this.agentInference.currentDelegation = {
         | 
| 299 | 
            -
                                agentName: inference.agentName,
         | 
| 300 | 
            -
                                sessionId: sessionId,
         | 
| 301 | 
            -
                                startIndex: index,
         | 
| 302 | 
            -
                                endIndex: null
         | 
| 303 | 
            -
                            };
         | 
| 304 | 
            -
                            console.log('Delegation started:', this.agentInference.currentDelegation);
         | 
| 305 | 
            -
                        } else if (inference.confidence === 'definitive' && inference.reason === 'SubagentStop event') {
         | 
| 306 | 
            -
                            // End of subagent delegation
         | 
| 307 | 
            -
                            if (this.agentInference.currentDelegation) {
         | 
| 308 | 
            -
                                this.agentInference.currentDelegation.endIndex = index;
         | 
| 309 | 
            -
                                console.log('Delegation ended:', this.agentInference.currentDelegation);
         | 
| 310 | 
            -
                                this.agentInference.currentDelegation = null;
         | 
| 311 | 
            -
                            }
         | 
| 312 | 
            -
                        }
         | 
| 313 | 
            -
                        
         | 
| 314 | 
            -
                        // Determine agent for this event based on context
         | 
| 315 | 
            -
                        let finalAgent = inference;
         | 
| 316 | 
            -
                        
         | 
| 317 | 
            -
                        // If we're in a delegation context and this event doesn't have high confidence agent info,
         | 
| 318 | 
            -
                        // inherit from delegation context
         | 
| 319 | 
            -
                        if (this.agentInference.currentDelegation && 
         | 
| 320 | 
            -
                            inference.confidence === 'default' && 
         | 
| 321 | 
            -
                            sessionId === this.agentInference.currentDelegation.sessionId) {
         | 
| 322 | 
            -
                            finalAgent = {
         | 
| 323 | 
            -
                                type: 'subagent',
         | 
| 324 | 
            -
                                confidence: 'inherited',
         | 
| 325 | 
            -
                                agentName: this.agentInference.currentDelegation.agentName,
         | 
| 326 | 
            -
                                reason: 'inherited from delegation context'
         | 
| 327 | 
            -
                            };
         | 
| 328 | 
            -
                        }
         | 
| 329 | 
            -
                        
         | 
| 330 | 
            -
                        // Store the inference result
         | 
| 331 | 
            -
                        this.agentInference.eventAgentMap.set(index, finalAgent);
         | 
| 332 | 
            -
                        
         | 
| 333 | 
            -
                        // Update session agent tracking
         | 
| 334 | 
            -
                        this.agentInference.sessionAgents.set(sessionId, finalAgent);
         | 
| 335 | 
            -
                        
         | 
| 336 | 
            -
                        // Debug first few inferences
         | 
| 337 | 
            -
                        if (index < 5) {
         | 
| 338 | 
            -
                            console.log(`Event ${index} agent inference:`, {
         | 
| 339 | 
            -
                                event_type: event.type,
         | 
| 340 | 
            -
                                subtype: event.subtype,
         | 
| 341 | 
            -
                                tool_name: event.tool_name,
         | 
| 342 | 
            -
                                inference: finalAgent
         | 
| 343 | 
            -
                            });
         | 
| 344 | 
            -
                        }
         | 
| 345 | 
            -
                    });
         | 
| 346 | 
            -
                    
         | 
| 347 | 
            -
                    console.log('Agent inference processing complete. Results:', {
         | 
| 348 | 
            -
                        total_events: events.length,
         | 
| 349 | 
            -
                        inferred_agents: this.agentInference.eventAgentMap.size,
         | 
| 350 | 
            -
                        unique_sessions: this.agentInference.sessionAgents.size
         | 
| 351 | 
            -
                    });
         | 
| 352 | 
            -
                }
         | 
| 353 | 
            -
             | 
| 354 | 
            -
                /**
         | 
| 355 | 
            -
                 * Get inferred agent for a specific event
         | 
| 356 | 
            -
                 * @param {number} eventIndex - Index of event in events array
         | 
| 357 | 
            -
                 * @returns {Object|null} - Agent inference result or null
         | 
| 358 | 
            -
                 */
         | 
| 359 | 
            -
                getInferredAgent(eventIndex) {
         | 
| 360 | 
            -
                    return this.agentInference.eventAgentMap.get(eventIndex) || null;
         | 
| 361 | 
            -
                }
         | 
| 362 | 
            -
             | 
| 363 | 
            -
                /**
         | 
| 364 | 
            -
                 * Get inferred agent for an event object
         | 
| 365 | 
            -
                 * @param {Object} event - Event object
         | 
| 366 | 
            -
                 * @returns {Object|null} - Agent inference result or null
         | 
| 367 | 
            -
                 */
         | 
| 368 | 
            -
                getInferredAgentForEvent(event) {
         | 
| 369 | 
            -
                    const events = this.eventViewer.events;
         | 
| 370 | 
            -
                    const eventIndex = events.indexOf(event);
         | 
| 371 | 
            -
                    if (eventIndex === -1) return null;
         | 
| 372 | 
            -
                    
         | 
| 373 | 
            -
                    return this.getInferredAgent(eventIndex);
         | 
| 374 | 
            -
                }
         | 
| 375 | 
            -
             | 
| 376 | 
            -
                /**
         | 
| 377 | 
            -
                 * Initialize all components
         | 
| 378 | 
            -
                 */
         | 
| 379 | 
            -
                initializeComponents() {
         | 
| 380 | 
            -
                    // Initialize socket client
         | 
| 381 | 
            -
                    this.socketClient = new SocketClient();
         | 
| 382 | 
            -
                    
         | 
| 383 | 
            -
                    // Initialize UI components
         | 
| 384 | 
            -
                    this.eventViewer = new EventViewer('events-list', this.socketClient);
         | 
| 385 | 
            -
                    this.moduleViewer = new ModuleViewer('module-content');
         | 
| 386 | 
            -
                    this.sessionManager = new SessionManager(this.socketClient);
         | 
| 387 | 
            -
                    this.hudVisualizer = new HUDVisualizer();
         | 
| 388 | 
            -
                    
         | 
| 389 | 
            -
                    // Store globally for backward compatibility
         | 
| 390 | 
            -
                    window.socketClient = this.socketClient;
         | 
| 391 | 
            -
                    window.eventViewer = this.eventViewer;
         | 
| 392 | 
            -
                    window.moduleViewer = this.moduleViewer;
         | 
| 393 | 
            -
                    window.sessionManager = this.sessionManager;
         | 
| 394 | 
            -
                    window.hudVisualizer = this.hudVisualizer;
         | 
| 395 | 
            -
                    
         | 
| 396 | 
            -
                    // Initialize HUD visualizer
         | 
| 397 | 
            -
                    this.hudVisualizer.initialize();
         | 
| 398 | 
            -
                    
         | 
| 399 | 
            -
                    // Setup component interactions
         | 
| 400 | 
            -
                    this.setupComponentInteractions();
         | 
| 401 | 
            -
                }
         | 
| 402 | 
            -
             | 
| 403 | 
            -
                /**
         | 
| 404 | 
            -
                 * Setup interactions between components
         | 
| 405 | 
            -
                 */
         | 
| 406 | 
            -
                setupComponentInteractions() {
         | 
| 407 | 
            -
                    // Socket connection status is now handled in the header connection status badge
         | 
| 408 | 
            -
                    // Footer now focuses on session-specific information
         | 
| 409 | 
            -
             | 
| 410 | 
            -
                    // Listen for socket events to update file operations and tool calls
         | 
| 411 | 
            -
                    this.socketClient.onEventUpdate((events) => {
         | 
| 412 | 
            -
                        this.updateFileOperations(events);
         | 
| 413 | 
            -
                        this.updateToolCalls(events);
         | 
| 414 | 
            -
                        // Process agent inference after events are updated
         | 
| 415 | 
            -
                        this.processAgentInference();
         | 
| 416 | 
            -
                        this.renderCurrentTab();
         | 
| 417 | 
            -
                        
         | 
| 418 | 
            -
                        // Process new events for HUD visualization
         | 
| 419 | 
            -
                        if (this.hudMode && this.hudVisualizer && events.length > 0) {
         | 
| 420 | 
            -
                            // Get the most recent event for HUD processing
         | 
| 421 | 
            -
                            const latestEvent = events[events.length - 1];
         | 
| 422 | 
            -
                            this.handleHUDEvent(latestEvent);
         | 
| 423 | 
            -
                        }
         | 
| 424 | 
            -
                        
         | 
| 425 | 
            -
                        // Auto-scroll events list if on events tab
         | 
| 426 | 
            -
                        if (this.currentTab === 'events') {
         | 
| 427 | 
            -
                            this.scrollListToBottom('events-list');
         | 
| 428 | 
            -
                        }
         | 
| 429 | 
            -
                    });
         | 
| 430 | 
            -
             | 
| 431 | 
            -
                    // Listen for connection status changes
         | 
| 432 | 
            -
                    document.addEventListener('socketConnectionStatus', (e) => {
         | 
| 433 | 
            -
                        this.updateConnectionStatus(e.detail.status, e.detail.type);
         | 
| 434 | 
            -
                        
         | 
| 435 | 
            -
                        // Set up git branch listener when connected
         | 
| 436 | 
            -
                        if (e.detail.type === 'connected' && this.socketClient && this.socketClient.socket) {
         | 
| 437 | 
            -
                            // Remove any existing listener first
         | 
| 438 | 
            -
                            this.socketClient.socket.off('git_branch_response');
         | 
| 439 | 
            -
                            
         | 
| 440 | 
            -
                            // Add the listener
         | 
| 441 | 
            -
                            this.socketClient.socket.on('git_branch_response', (data) => {
         | 
| 442 | 
            -
                                if (data.success) {
         | 
| 443 | 
            -
                                    const footerBranch = document.getElementById('footer-git-branch');
         | 
| 444 | 
            -
                                    if (footerBranch) {
         | 
| 445 | 
            -
                                        footerBranch.textContent = data.branch;
         | 
| 446 | 
            -
                                    }
         | 
| 447 | 
            -
                                } else {
         | 
| 448 | 
            -
                                    const footerBranch = document.getElementById('footer-git-branch');
         | 
| 449 | 
            -
                                    if (footerBranch) {
         | 
| 450 | 
            -
                                        footerBranch.textContent = 'No Git';
         | 
| 451 | 
            -
                                    }
         | 
| 452 | 
            -
                                }
         | 
| 453 | 
            -
                            });
         | 
| 454 | 
            -
                            
         | 
| 455 | 
            -
                            // Request git branch for current working directory
         | 
| 456 | 
            -
                            this.updateGitBranch(this.currentWorkingDir);
         | 
| 457 | 
            -
                        }
         | 
| 458 | 
            -
                    });
         | 
| 459 | 
            -
             | 
| 460 | 
            -
                    // Listen for session filter changes to update dropdown options
         | 
| 461 | 
            -
                    document.addEventListener('sessionFilterChanged', (e) => {
         | 
| 462 | 
            -
                        console.log('Session filter changed, re-rendering current tab:', this.currentTab);
         | 
| 463 | 
            -
                        this.renderCurrentTab();
         | 
| 464 | 
            -
                        // Update HUD button state based on session selection
         | 
| 465 | 
            -
                        this.updateHUDButtonState();
         | 
| 466 | 
            -
                    });
         | 
| 467 | 
            -
                }
         | 
| 468 | 
            -
             | 
| 469 | 
            -
                /**
         | 
| 470 | 
            -
                 * Setup general event handlers
         | 
| 471 | 
            -
                 */
         | 
| 472 | 
            -
                setupEventHandlers() {
         | 
| 473 | 
            -
                    // Connection controls
         | 
| 474 | 
            -
                    const connectBtn = document.getElementById('connect-btn');
         | 
| 475 | 
            -
                    const disconnectBtn = document.getElementById('disconnect-btn');
         | 
| 476 | 
            -
                    const portInput = document.getElementById('port-input');
         | 
| 477 | 
            -
             | 
| 478 | 
            -
                    if (connectBtn) {
         | 
| 479 | 
            -
                        connectBtn.addEventListener('click', () => {
         | 
| 480 | 
            -
                            const port = portInput ? portInput.value : '8765';
         | 
| 481 | 
            -
                            this.socketClient.connect(port);
         | 
| 482 | 
            -
                        });
         | 
| 483 | 
            -
                    }
         | 
| 484 | 
            -
             | 
| 485 | 
            -
                    if (disconnectBtn) {
         | 
| 486 | 
            -
                        disconnectBtn.addEventListener('click', () => {
         | 
| 487 | 
            -
                            this.socketClient.disconnect();
         | 
| 488 | 
            -
                        });
         | 
| 489 | 
            -
                    }
         | 
| 490 | 
            -
             | 
| 491 | 
            -
                    // Connection toggle button
         | 
| 492 | 
            -
                    const connectionToggleBtn = document.getElementById('connection-toggle-btn');
         | 
| 493 | 
            -
                    if (connectionToggleBtn) {
         | 
| 494 | 
            -
                        connectionToggleBtn.addEventListener('click', () => {
         | 
| 495 | 
            -
                            this.toggleConnectionControls();
         | 
| 496 | 
            -
                        });
         | 
| 497 | 
            -
                    }
         | 
| 498 | 
            -
             | 
| 499 | 
            -
                    // Working directory controls
         | 
| 500 | 
            -
                    const changeDirBtn = document.getElementById('change-dir-btn');
         | 
| 501 | 
            -
                    const workingDirPath = document.getElementById('working-dir-path');
         | 
| 502 | 
            -
                    
         | 
| 503 | 
            -
                    if (changeDirBtn) {
         | 
| 504 | 
            -
                        changeDirBtn.addEventListener('click', () => {
         | 
| 505 | 
            -
                            this.showChangeDirDialog();
         | 
| 506 | 
            -
                        });
         | 
| 507 | 
            -
                    }
         | 
| 508 | 
            -
                    
         | 
| 509 | 
            -
                    if (workingDirPath) {
         | 
| 510 | 
            -
                        workingDirPath.addEventListener('click', () => {
         | 
| 511 | 
            -
                            this.showChangeDirDialog();
         | 
| 512 | 
            -
                        });
         | 
| 513 | 
            -
                    }
         | 
| 514 | 
            -
                    
         | 
| 515 | 
            -
                    // Action buttons
         | 
| 516 | 
            -
                    const clearBtn = document.querySelector('button[onclick="clearEvents()"]');
         | 
| 517 | 
            -
                    const exportBtn = document.getElementById('export-btn');
         | 
| 518 | 
            -
                    
         | 
| 519 | 
            -
                    if (clearBtn) {
         | 
| 520 | 
            -
                        clearBtn.addEventListener('click', () => {
         | 
| 521 | 
            -
                            this.clearEvents();
         | 
| 522 | 
            -
                        });
         | 
| 523 | 
            -
                    }
         | 
| 524 | 
            -
                    
         | 
| 525 | 
            -
                    if (exportBtn) {
         | 
| 526 | 
            -
                        exportBtn.addEventListener('click', () => {
         | 
| 527 | 
            -
                            this.exportEvents();
         | 
| 528 | 
            -
                        });
         | 
| 529 | 
            -
                    }
         | 
| 530 | 
            -
             | 
| 531 | 
            -
                    // HUD toggle button
         | 
| 532 | 
            -
                    const hudToggleBtn = document.getElementById('hud-toggle-btn');
         | 
| 533 | 
            -
                    if (hudToggleBtn) {
         | 
| 534 | 
            -
                        hudToggleBtn.addEventListener('click', () => {
         | 
| 535 | 
            -
                            this.toggleHUD();
         | 
| 536 | 
            -
                        });
         | 
| 537 | 
            -
                    }
         | 
| 538 | 
            -
             | 
| 539 | 
            -
                    // Clear selection button
         | 
| 540 | 
            -
                    const clearSelectionBtn = document.querySelector('button[onclick="clearSelection()"]');
         | 
| 541 | 
            -
                    if (clearSelectionBtn) {
         | 
| 542 | 
            -
                        clearSelectionBtn.addEventListener('click', () => {
         | 
| 543 | 
            -
                            this.clearSelection();
         | 
| 544 | 
            -
                        });
         | 
| 545 | 
            -
                    }
         | 
| 546 | 
            -
             | 
| 547 | 
            -
                    // Tab-specific filters
         | 
| 548 | 
            -
                    this.setupTabFilters();
         | 
| 549 | 
            -
                }
         | 
| 550 | 
            -
             | 
| 551 | 
            -
                /**
         | 
| 552 | 
            -
                 * Setup filtering for each tab
         | 
| 553 | 
            -
                 */
         | 
| 554 | 
            -
                setupTabFilters() {
         | 
| 555 | 
            -
                    // Agents tab filters
         | 
| 556 | 
            -
                    const agentsSearchInput = document.getElementById('agents-search-input');
         | 
| 557 | 
            -
                    const agentsTypeFilter = document.getElementById('agents-type-filter');
         | 
| 558 | 
            -
                    
         | 
| 559 | 
            -
                    if (agentsSearchInput) {
         | 
| 560 | 
            -
                        agentsSearchInput.addEventListener('input', () => {
         | 
| 561 | 
            -
                            if (this.currentTab === 'agents') this.renderCurrentTab();
         | 
| 562 | 
            -
                        });
         | 
| 563 | 
            -
                    }
         | 
| 564 | 
            -
                    
         | 
| 565 | 
            -
                    if (agentsTypeFilter) {
         | 
| 566 | 
            -
                        agentsTypeFilter.addEventListener('change', () => {
         | 
| 567 | 
            -
                            if (this.currentTab === 'agents') this.renderCurrentTab();
         | 
| 568 | 
            -
                        });
         | 
| 569 | 
            -
                    }
         | 
| 570 | 
            -
             | 
| 571 | 
            -
                    // Tools tab filters
         | 
| 572 | 
            -
                    const toolsSearchInput = document.getElementById('tools-search-input');
         | 
| 573 | 
            -
                    const toolsTypeFilter = document.getElementById('tools-type-filter');
         | 
| 574 | 
            -
                    
         | 
| 575 | 
            -
                    if (toolsSearchInput) {
         | 
| 576 | 
            -
                        toolsSearchInput.addEventListener('input', () => {
         | 
| 577 | 
            -
                            if (this.currentTab === 'tools') this.renderCurrentTab();
         | 
| 578 | 
            -
                        });
         | 
| 579 | 
            -
                    }
         | 
| 580 | 
            -
                    
         | 
| 581 | 
            -
                    if (toolsTypeFilter) {
         | 
| 582 | 
            -
                        toolsTypeFilter.addEventListener('change', () => {
         | 
| 583 | 
            -
                            if (this.currentTab === 'tools') this.renderCurrentTab();
         | 
| 584 | 
            -
                        });
         | 
| 585 | 
            -
                    }
         | 
| 586 | 
            -
             | 
| 587 | 
            -
                    // Files tab filters
         | 
| 588 | 
            -
                    const filesSearchInput = document.getElementById('files-search-input');
         | 
| 589 | 
            -
                    const filesTypeFilter = document.getElementById('files-type-filter');
         | 
| 590 | 
            -
                    
         | 
| 591 | 
            -
                    if (filesSearchInput) {
         | 
| 592 | 
            -
                        filesSearchInput.addEventListener('input', () => {
         | 
| 593 | 
            -
                            if (this.currentTab === 'files') this.renderCurrentTab();
         | 
| 594 | 
            -
                        });
         | 
| 595 | 
            -
                    }
         | 
| 596 | 
            -
                    
         | 
| 597 | 
            -
                    if (filesTypeFilter) {
         | 
| 598 | 
            -
                        filesTypeFilter.addEventListener('change', () => {
         | 
| 599 | 
            -
                            if (this.currentTab === 'files') this.renderCurrentTab();
         | 
| 600 | 
            -
                        });
         | 
| 601 | 
            -
                    }
         | 
| 602 | 
            -
                }
         | 
| 603 | 
            -
             | 
| 604 | 
            -
                /**
         | 
| 605 | 
            -
                 * Populate filter dropdown with unique values from data
         | 
| 606 | 
            -
                 * @param {string} selectId - ID of the select element
         | 
| 607 | 
            -
                 * @param {Array} values - Array of unique values to populate
         | 
| 608 | 
            -
                 * @param {string} allOption - Text for the "All" option
         | 
| 609 | 
            -
                 */
         | 
| 610 | 
            -
                populateFilterDropdown(selectId, values, allOption) {
         | 
| 611 | 
            -
                    const select = document.getElementById(selectId);
         | 
| 612 | 
            -
                    if (!select) return;
         | 
| 613 | 
            -
             | 
| 614 | 
            -
                    // Store current selection
         | 
| 615 | 
            -
                    const currentValue = select.value;
         | 
| 616 | 
            -
             | 
| 617 | 
            -
                    // Clear existing options except the first "All" option
         | 
| 618 | 
            -
                    select.innerHTML = `<option value="">${allOption}</option>`;
         | 
| 619 | 
            -
             | 
| 620 | 
            -
                    // Add unique values, sorted alphabetically
         | 
| 621 | 
            -
                    const sortedValues = [...values].sort();
         | 
| 622 | 
            -
                    sortedValues.forEach(value => {
         | 
| 623 | 
            -
                        const option = document.createElement('option');
         | 
| 624 | 
            -
                        option.value = value;
         | 
| 625 | 
            -
                        option.textContent = value;
         | 
| 626 | 
            -
                        select.appendChild(option);
         | 
| 627 | 
            -
                    });
         | 
| 628 | 
            -
             | 
| 629 | 
            -
                    // Restore selection if it still exists
         | 
| 630 | 
            -
                    if (currentValue && sortedValues.includes(currentValue)) {
         | 
| 631 | 
            -
                        select.value = currentValue;
         | 
| 632 | 
            -
                    }
         | 
| 633 | 
            -
                }
         | 
| 634 | 
            -
             | 
| 635 | 
            -
                /**
         | 
| 636 | 
            -
                 * Setup tab navigation
         | 
| 637 | 
            -
                 */
         | 
| 638 | 
            -
                setupTabNavigation() {
         | 
| 639 | 
            -
                    const tabButtons = document.querySelectorAll('.tab-button');
         | 
| 640 | 
            -
                    tabButtons.forEach(button => {
         | 
| 641 | 
            -
                        button.addEventListener('click', () => {
         | 
| 642 | 
            -
                            const tabName = this.getTabNameFromButton(button);
         | 
| 643 | 
            -
                            this.switchTab(tabName);
         | 
| 644 | 
            -
                        });
         | 
| 645 | 
            -
                    });
         | 
| 646 | 
            -
                }
         | 
| 647 | 
            -
             | 
| 648 | 
            -
                /**
         | 
| 649 | 
            -
                 * Setup unified keyboard navigation for all tabs
         | 
| 650 | 
            -
                 */
         | 
| 651 | 
            -
                setupUnifiedKeyboardNavigation() {
         | 
| 652 | 
            -
                    document.addEventListener('keydown', (e) => {
         | 
| 653 | 
            -
                        // Only handle navigation if no input is focused
         | 
| 654 | 
            -
                        if (document.activeElement && 
         | 
| 655 | 
            -
                            (document.activeElement.tagName === 'INPUT' || 
         | 
| 656 | 
            -
                             document.activeElement.tagName === 'TEXTAREA' || 
         | 
| 657 | 
            -
                             document.activeElement.tagName === 'SELECT')) {
         | 
| 658 | 
            -
                            return;
         | 
| 659 | 
            -
                        }
         | 
| 660 | 
            -
             | 
| 661 | 
            -
                        if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
         | 
| 662 | 
            -
                            e.preventDefault();
         | 
| 663 | 
            -
                            this.handleUnifiedArrowNavigation(e.key === 'ArrowDown' ? 1 : -1);
         | 
| 664 | 
            -
                        } else if (e.key === 'Enter') {
         | 
| 665 | 
            -
                            e.preventDefault();
         | 
| 666 | 
            -
                            this.handleUnifiedEnterKey();
         | 
| 667 | 
            -
                        } else if (e.key === 'Escape') {
         | 
| 668 | 
            -
                            e.preventDefault();
         | 
| 669 | 
            -
                            this.clearUnifiedSelection();
         | 
| 670 | 
            -
                        }
         | 
| 671 | 
            -
                    });
         | 
| 672 | 
            -
                }
         | 
| 673 | 
            -
             | 
| 674 | 
            -
                /**
         | 
| 675 | 
            -
                 * Get tab name from button text
         | 
| 676 | 
            -
                 */
         | 
| 677 | 
            -
                getTabNameFromButton(button) {
         | 
| 678 | 
            -
                    const text = button.textContent.toLowerCase();
         | 
| 679 | 
            -
                    if (text.includes('events')) return 'events';
         | 
| 680 | 
            -
                    if (text.includes('agents')) return 'agents';
         | 
| 681 | 
            -
                    if (text.includes('tools')) return 'tools';
         | 
| 682 | 
            -
                    if (text.includes('files')) return 'files';
         | 
| 683 | 
            -
                    return 'events';
         | 
| 684 | 
            -
                }
         | 
| 685 | 
            -
             | 
| 686 | 
            -
                /**
         | 
| 687 | 
            -
                 * Initialize from URL parameters
         | 
| 688 | 
            -
                 */
         | 
| 689 | 
            -
                initializeFromURL() {
         | 
| 690 | 
            -
                    const urlParams = new URLSearchParams(window.location.search);
         | 
| 691 | 
            -
                    const defaultPort = urlParams.get('port') || '8765';
         | 
| 692 | 
            -
                    const autoConnect = urlParams.get('autoconnect');
         | 
| 693 | 
            -
                    
         | 
| 694 | 
            -
                    const portInput = document.getElementById('port-input');
         | 
| 695 | 
            -
                    if (portInput) {
         | 
| 696 | 
            -
                        portInput.value = defaultPort;
         | 
| 697 | 
            -
                    }
         | 
| 698 | 
            -
                    
         | 
| 699 | 
            -
                    // Auto-connect logic:
         | 
| 700 | 
            -
                    // - Connect if autoconnect=true (explicit)
         | 
| 701 | 
            -
                    // - Connect by default unless autoconnect=false (explicit)
         | 
| 702 | 
            -
                    // - Don't connect if already connected or connecting
         | 
| 703 | 
            -
                    const shouldAutoConnect = autoConnect === 'true' || (autoConnect !== 'false' && autoConnect === null);
         | 
| 704 | 
            -
                    
         | 
| 705 | 
            -
                    if (shouldAutoConnect && !this.socketClient.isConnected && !this.socketClient.isConnecting) {
         | 
| 706 | 
            -
                        console.log('Auto-connecting to Socket.IO server on page load...');
         | 
| 707 | 
            -
                        this.socketClient.connect(defaultPort);
         | 
| 708 | 
            -
                    }
         | 
| 709 | 
            -
                }
         | 
| 710 | 
            -
             | 
| 711 | 
            -
                /**
         | 
| 712 | 
            -
                 * Switch to a different tab
         | 
| 713 | 
            -
                 */
         | 
| 714 | 
            -
                switchTab(tabName) {
         | 
| 715 | 
            -
                    console.log(`[DEBUG] switchTab called with tabName: ${tabName}`);
         | 
| 716 | 
            -
                    this.currentTab = tabName;
         | 
| 717 | 
            -
                    
         | 
| 718 | 
            -
                    // Update tab buttons
         | 
| 719 | 
            -
                    document.querySelectorAll('.tab-button').forEach(btn => {
         | 
| 720 | 
            -
                        btn.classList.remove('active');
         | 
| 721 | 
            -
                        if (this.getTabNameFromButton(btn) === tabName) {
         | 
| 722 | 
            -
                            btn.classList.add('active');
         | 
| 723 | 
            -
                        }
         | 
| 724 | 
            -
                    });
         | 
| 725 | 
            -
                    
         | 
| 726 | 
            -
                    // Update tab content
         | 
| 727 | 
            -
                    document.querySelectorAll('.tab-content').forEach(content => {
         | 
| 728 | 
            -
                        content.classList.remove('active');
         | 
| 729 | 
            -
                    });
         | 
| 730 | 
            -
                    
         | 
| 731 | 
            -
                    const activeTab = document.getElementById(`${tabName}-tab`);
         | 
| 732 | 
            -
                    console.log(`[DEBUG] Active tab element found:`, activeTab);
         | 
| 733 | 
            -
                    if (activeTab) {
         | 
| 734 | 
            -
                        activeTab.classList.add('active');
         | 
| 735 | 
            -
                    }
         | 
| 736 | 
            -
                    
         | 
| 737 | 
            -
                    // Render content for the active tab
         | 
| 738 | 
            -
                    console.log(`[DEBUG] About to render current tab: ${tabName}`);
         | 
| 739 | 
            -
                    this.renderCurrentTab();
         | 
| 740 | 
            -
                    
         | 
| 741 | 
            -
                    // Auto-scroll to bottom after tab content is rendered
         | 
| 742 | 
            -
                    const listId = `${tabName}-list`;
         | 
| 743 | 
            -
                    console.log(`[DEBUG] About to scroll list with ID: ${listId}`);
         | 
| 744 | 
            -
                    this.scrollListToBottom(listId);
         | 
| 745 | 
            -
                    
         | 
| 746 | 
            -
                    // Fallback: Try again with longer delay in case content takes time to render
         | 
| 747 | 
            -
                    setTimeout(() => {
         | 
| 748 | 
            -
                        console.log(`[DEBUG] Fallback scroll attempt for ${listId}`);
         | 
| 749 | 
            -
                        this.scrollListToBottom(listId);
         | 
| 750 | 
            -
                    }, 200);
         | 
| 751 | 
            -
                    
         | 
| 752 | 
            -
                    console.log(`[DEBUG] Switched to ${tabName} tab`);
         | 
| 753 | 
            -
                }
         | 
| 754 | 
            -
             | 
| 755 | 
            -
                /**
         | 
| 756 | 
            -
                 * Handle unified arrow key navigation across all tabs
         | 
| 757 | 
            -
                 * @param {number} direction - Direction: 1 for down, -1 for up
         | 
| 758 | 
            -
                 */
         | 
| 759 | 
            -
                handleUnifiedArrowNavigation(direction) {
         | 
| 760 | 
            -
                    const tabNav = this.tabNavigation[this.currentTab];
         | 
| 761 | 
            -
                    if (!tabNav) return;
         | 
| 762 | 
            -
             | 
| 763 | 
            -
                    // Update items list for current tab
         | 
| 764 | 
            -
                    this.updateTabNavigationItems();
         | 
| 765 | 
            -
             | 
| 766 | 
            -
                    if (tabNav.items.length === 0) return;
         | 
| 767 | 
            -
             | 
| 768 | 
            -
                    // Calculate new index
         | 
| 769 | 
            -
                    let newIndex = tabNav.selectedIndex + direction;
         | 
| 770 | 
            -
                    
         | 
| 771 | 
            -
                    // Wrap around
         | 
| 772 | 
            -
                    if (newIndex >= tabNav.items.length) {
         | 
| 773 | 
            -
                        newIndex = 0;
         | 
| 774 | 
            -
                    } else if (newIndex < 0) {
         | 
| 775 | 
            -
                        newIndex = tabNav.items.length - 1;
         | 
| 776 | 
            -
                    }
         | 
| 777 | 
            -
             | 
| 778 | 
            -
                    // Update selection
         | 
| 779 | 
            -
                    this.selectCardByIndex(this.currentTab, newIndex);
         | 
| 780 | 
            -
                }
         | 
| 781 | 
            -
             | 
| 782 | 
            -
                /**
         | 
| 783 | 
            -
                 * Handle unified Enter key across all tabs
         | 
| 784 | 
            -
                 */
         | 
| 785 | 
            -
                handleUnifiedEnterKey() {
         | 
| 786 | 
            -
                    const tabNav = this.tabNavigation[this.currentTab];
         | 
| 787 | 
            -
                    if (!tabNav || tabNav.selectedIndex === -1) return;
         | 
| 788 | 
            -
             | 
| 789 | 
            -
                    // Trigger click on the selected item
         | 
| 790 | 
            -
                    const selectedElement = tabNav.items[tabNav.selectedIndex];
         | 
| 791 | 
            -
                    if (selectedElement && selectedElement.onclick) {
         | 
| 792 | 
            -
                        selectedElement.click();
         | 
| 793 | 
            -
                    }
         | 
| 794 | 
            -
                }
         | 
| 795 | 
            -
             | 
| 796 | 
            -
                /**
         | 
| 797 | 
            -
                 * Clear unified selection across all tabs
         | 
| 798 | 
            -
                 */
         | 
| 799 | 
            -
                clearUnifiedSelection() {
         | 
| 800 | 
            -
                    // Clear all tab navigation states
         | 
| 801 | 
            -
                    Object.keys(this.tabNavigation).forEach(tabName => {
         | 
| 802 | 
            -
                        this.tabNavigation[tabName].selectedIndex = -1;
         | 
| 803 | 
            -
                    });
         | 
| 804 | 
            -
                    
         | 
| 805 | 
            -
                    // Clear card selection
         | 
| 806 | 
            -
                    this.clearCardSelection();
         | 
| 807 | 
            -
                    
         | 
| 808 | 
            -
                    // Clear EventViewer selection if it exists
         | 
| 809 | 
            -
                    if (this.eventViewer) {
         | 
| 810 | 
            -
                        this.eventViewer.clearSelection();
         | 
| 811 | 
            -
                    }
         | 
| 812 | 
            -
                    
         | 
| 813 | 
            -
                    // Clear module viewer
         | 
| 814 | 
            -
                    if (this.moduleViewer) {
         | 
| 815 | 
            -
                        this.moduleViewer.clear();
         | 
| 816 | 
            -
                    }
         | 
| 817 | 
            -
                }
         | 
| 818 | 
            -
             | 
| 819 | 
            -
                /**
         | 
| 820 | 
            -
                 * Update items list for current tab navigation
         | 
| 821 | 
            -
                 */
         | 
| 822 | 
            -
                updateTabNavigationItems() {
         | 
| 823 | 
            -
                    const tabNav = this.tabNavigation[this.currentTab];
         | 
| 824 | 
            -
                    if (!tabNav) return;
         | 
| 825 | 
            -
             | 
| 826 | 
            -
                    let containerSelector;
         | 
| 827 | 
            -
                    switch (this.currentTab) {
         | 
| 828 | 
            -
                        case 'events':
         | 
| 829 | 
            -
                            containerSelector = '#events-list .event-item';
         | 
| 830 | 
            -
                            break;
         | 
| 831 | 
            -
                        case 'agents':
         | 
| 832 | 
            -
                            containerSelector = '#agents-list .event-item';
         | 
| 833 | 
            -
                            break;
         | 
| 834 | 
            -
                        case 'tools':
         | 
| 835 | 
            -
                            containerSelector = '#tools-list .event-item';
         | 
| 836 | 
            -
                            break;
         | 
| 837 | 
            -
                        case 'files':
         | 
| 838 | 
            -
                            containerSelector = '#files-list .file-item, #files-list .event-item';
         | 
| 839 | 
            -
                            break;
         | 
| 840 | 
            -
                    }
         | 
| 841 | 
            -
             | 
| 842 | 
            -
                    if (containerSelector) {
         | 
| 843 | 
            -
                        tabNav.items = Array.from(document.querySelectorAll(containerSelector));
         | 
| 844 | 
            -
                    }
         | 
| 845 | 
            -
                }
         | 
| 846 | 
            -
             | 
| 847 | 
            -
                /**
         | 
| 848 | 
            -
                 * Select a card by index in the specified tab
         | 
| 849 | 
            -
                 * @param {string} tabName - Tab name
         | 
| 850 | 
            -
                 * @param {number} index - Index of item to select
         | 
| 851 | 
            -
                 */
         | 
| 852 | 
            -
                selectCardByIndex(tabName, index) {
         | 
| 853 | 
            -
                    const tabNav = this.tabNavigation[tabName];
         | 
| 854 | 
            -
                    if (!tabNav || index < 0 || index >= tabNav.items.length) return;
         | 
| 855 | 
            -
             | 
| 856 | 
            -
                    // Update navigation state
         | 
| 857 | 
            -
                    tabNav.selectedIndex = index;
         | 
| 858 | 
            -
                    
         | 
| 859 | 
            -
                    // Update visual selection
         | 
| 860 | 
            -
                    this.updateUnifiedSelectionUI();
         | 
| 861 | 
            -
                    
         | 
| 862 | 
            -
                    // Scroll selected item into view
         | 
| 863 | 
            -
                    const selectedElement = tabNav.items[index];
         | 
| 864 | 
            -
                    if (selectedElement) {
         | 
| 865 | 
            -
                        selectedElement.scrollIntoView({ 
         | 
| 866 | 
            -
                            behavior: 'smooth', 
         | 
| 867 | 
            -
                            block: 'nearest' 
         | 
| 868 | 
            -
                        });
         | 
| 869 | 
            -
                    }
         | 
| 870 | 
            -
             | 
| 871 | 
            -
                    // Update details view based on tab
         | 
| 872 | 
            -
                    this.showCardDetails(tabName, index);
         | 
| 873 | 
            -
                }
         | 
| 874 | 
            -
             | 
| 875 | 
            -
                /**
         | 
| 876 | 
            -
                 * Update visual selection UI for current tab
         | 
| 877 | 
            -
                 */
         | 
| 878 | 
            -
                updateUnifiedSelectionUI() {
         | 
| 879 | 
            -
                    const tabNav = this.tabNavigation[this.currentTab];
         | 
| 880 | 
            -
                    if (!tabNav) return;
         | 
| 881 | 
            -
             | 
| 882 | 
            -
                    // Clear all selections in current tab
         | 
| 883 | 
            -
                    tabNav.items.forEach((item, index) => {
         | 
| 884 | 
            -
                        item.classList.toggle('selected', index === tabNav.selectedIndex);
         | 
| 885 | 
            -
                    });
         | 
| 886 | 
            -
                }
         | 
| 887 | 
            -
             | 
| 888 | 
            -
                /**
         | 
| 889 | 
            -
                 * Show card details based on tab and index
         | 
| 890 | 
            -
                 * @param {string} tabName - Tab name
         | 
| 891 | 
            -
                 * @param {number} index - Index of item
         | 
| 892 | 
            -
                 */
         | 
| 893 | 
            -
                showCardDetails(tabName, index) {
         | 
| 894 | 
            -
                    switch (tabName) {
         | 
| 895 | 
            -
                        case 'events':
         | 
| 896 | 
            -
                            // Use EventViewer's existing method
         | 
| 897 | 
            -
                            if (this.eventViewer) {
         | 
| 898 | 
            -
                                this.eventViewer.showEventDetails(index);
         | 
| 899 | 
            -
                            }
         | 
| 900 | 
            -
                            break;
         | 
| 901 | 
            -
                        case 'agents':
         | 
| 902 | 
            -
                            this.showAgentDetailsByIndex(index);
         | 
| 903 | 
            -
                            break;
         | 
| 904 | 
            -
                        case 'tools':
         | 
| 905 | 
            -
                            this.showToolDetailsByIndex(index);
         | 
| 906 | 
            -
                            break;
         | 
| 907 | 
            -
                        case 'files':
         | 
| 908 | 
            -
                            this.showFileDetailsByIndex(index);
         | 
| 909 | 
            -
                            break;
         | 
| 910 | 
            -
                    }
         | 
| 911 | 
            -
                }
         | 
| 912 | 
            -
             | 
| 913 | 
            -
                /**
         | 
| 914 | 
            -
                 * Select a card and update the UI
         | 
| 915 | 
            -
                 * @param {string} tabName - Tab name (events, agents, tools, files)
         | 
| 916 | 
            -
                 * @param {number} index - Index of the item in that tab
         | 
| 917 | 
            -
                 * @param {string} type - Type of item (event, agent, tool, file)
         | 
| 918 | 
            -
                 * @param {Object} data - The data object for the selected item
         | 
| 919 | 
            -
                 */
         | 
| 920 | 
            -
                selectCard(tabName, index, type, data) {
         | 
| 921 | 
            -
                    // Clear previous selection
         | 
| 922 | 
            -
                    this.clearCardSelection();
         | 
| 923 | 
            -
                    
         | 
| 924 | 
            -
                    // Update selection state
         | 
| 925 | 
            -
                    this.selectedCard = {
         | 
| 926 | 
            -
                        tab: tabName,
         | 
| 927 | 
            -
                        index: index,
         | 
| 928 | 
            -
                        type: type,
         | 
| 929 | 
            -
                        data: data
         | 
| 930 | 
            -
                    };
         | 
| 931 | 
            -
                    
         | 
| 932 | 
            -
                    // Update visual selection in the current tab
         | 
| 933 | 
            -
                    this.updateCardSelectionUI();
         | 
| 934 | 
            -
                    
         | 
| 935 | 
            -
                    console.log('Card selected:', this.selectedCard);
         | 
| 936 | 
            -
                }
         | 
| 937 | 
            -
                
         | 
| 938 | 
            -
                /**
         | 
| 939 | 
            -
                 * Clear card selection
         | 
| 940 | 
            -
                 */
         | 
| 941 | 
            -
                clearCardSelection() {
         | 
| 942 | 
            -
                    // Clear visual selection from all tabs
         | 
| 943 | 
            -
                    document.querySelectorAll('.event-item.selected, .file-item.selected').forEach(el => {
         | 
| 944 | 
            -
                        el.classList.remove('selected');
         | 
| 945 | 
            -
                    });
         | 
| 946 | 
            -
                    
         | 
| 947 | 
            -
                    // Reset selection state
         | 
| 948 | 
            -
                    this.selectedCard = {
         | 
| 949 | 
            -
                        tab: null,
         | 
| 950 | 
            -
                        index: null,
         | 
| 951 | 
            -
                        type: null,
         | 
| 952 | 
            -
                        data: null
         | 
| 953 | 
            -
                    };
         | 
| 954 | 
            -
                }
         | 
| 955 | 
            -
                
         | 
| 956 | 
            -
                /**
         | 
| 957 | 
            -
                 * Update visual selection in the current tab
         | 
| 958 | 
            -
                 */
         | 
| 959 | 
            -
                updateCardSelectionUI() {
         | 
| 960 | 
            -
                    if (!this.selectedCard.tab || this.selectedCard.index === null) return;
         | 
| 961 | 
            -
                    
         | 
| 962 | 
            -
                    // Get the list container for the selected tab
         | 
| 963 | 
            -
                    let listContainer;
         | 
| 964 | 
            -
                    switch (this.selectedCard.tab) {
         | 
| 965 | 
            -
                        case 'events':
         | 
| 966 | 
            -
                            listContainer = document.getElementById('events-list');
         | 
| 967 | 
            -
                            break;
         | 
| 968 | 
            -
                        case 'agents':
         | 
| 969 | 
            -
                            listContainer = document.getElementById('agents-list');
         | 
| 970 | 
            -
                            break;
         | 
| 971 | 
            -
                        case 'tools':
         | 
| 972 | 
            -
                            listContainer = document.getElementById('tools-list');
         | 
| 973 | 
            -
                            break;
         | 
| 974 | 
            -
                        case 'files':
         | 
| 975 | 
            -
                            listContainer = document.getElementById('files-list');
         | 
| 976 | 
            -
                            break;
         | 
| 977 | 
            -
                    }
         | 
| 978 | 
            -
                    
         | 
| 979 | 
            -
                    if (listContainer) {
         | 
| 980 | 
            -
                        const cards = listContainer.querySelectorAll('.event-item, .file-item');
         | 
| 981 | 
            -
                        cards.forEach((card, index) => {
         | 
| 982 | 
            -
                            card.classList.toggle('selected', index === this.selectedCard.index);
         | 
| 983 | 
            -
                        });
         | 
| 984 | 
            -
                    }
         | 
| 985 | 
            -
                }
         | 
| 986 | 
            -
             | 
| 987 | 
            -
                /**
         | 
| 988 | 
            -
                 * Show agent details by index in the current filtered list
         | 
| 989 | 
            -
                 * @param {number} index - Index in the filtered agents list
         | 
| 990 | 
            -
                 */
         | 
| 991 | 
            -
                showAgentDetailsByIndex(index) {
         | 
| 992 | 
            -
                    // Use stored filtered agent events instead of recalculating
         | 
| 993 | 
            -
                    const agentEvents = this.agentEvents;
         | 
| 994 | 
            -
             | 
| 995 | 
            -
                    if (index < 0 || index >= agentEvents.length) {
         | 
| 996 | 
            -
                        console.warn('Invalid agent index:', index, 'Available agents:', agentEvents.length);
         | 
| 997 | 
            -
                        return;
         | 
| 998 | 
            -
                    }
         | 
| 999 | 
            -
             | 
| 1000 | 
            -
                    const event = agentEvents[index];
         | 
| 1001 | 
            -
                    const eventIndex = this.eventViewer.events.indexOf(event);
         | 
| 1002 | 
            -
                    this.showAgentDetails(index, eventIndex);
         | 
| 1003 | 
            -
                }
         | 
| 1004 | 
            -
             | 
| 1005 | 
            -
                /**
         | 
| 1006 | 
            -
                 * Show tool details by index in the current filtered list
         | 
| 1007 | 
            -
                 * @param {number} index - Index in the filtered tool calls list
         | 
| 1008 | 
            -
                 */
         | 
| 1009 | 
            -
                showToolDetailsByIndex(index) {
         | 
| 1010 | 
            -
                    // Get filtered tool calls array (same as renderTools)
         | 
| 1011 | 
            -
                    let toolCallsArray = Array.from(this.toolCalls.entries())
         | 
| 1012 | 
            -
                        .filter(([key, toolCall]) => {
         | 
| 1013 | 
            -
                            return toolCall.tool_name && (toolCall.pre_event || toolCall.post_event);
         | 
| 1014 | 
            -
                        })
         | 
| 1015 | 
            -
                        .sort((a, b) => {
         | 
| 1016 | 
            -
                            const timeA = new Date(a[1].timestamp || 0);
         | 
| 1017 | 
            -
                            const timeB = new Date(b[1].timestamp || 0);
         | 
| 1018 | 
            -
                            return timeA - timeB;
         | 
| 1019 | 
            -
                        });
         | 
| 1020 | 
            -
             | 
| 1021 | 
            -
                    // Apply tab-specific filters
         | 
| 1022 | 
            -
                    toolCallsArray = this.applyToolCallFilters(toolCallsArray);
         | 
| 1023 | 
            -
             | 
| 1024 | 
            -
                    if (index < 0 || index >= toolCallsArray.length) return;
         | 
| 1025 | 
            -
             | 
| 1026 | 
            -
                    const [toolCallKey] = toolCallsArray[index];
         | 
| 1027 | 
            -
                    this.showToolCallDetails(toolCallKey);
         | 
| 1028 | 
            -
                }
         | 
| 1029 | 
            -
             | 
| 1030 | 
            -
                /**
         | 
| 1031 | 
            -
                 * Show file details by index in the current filtered list
         | 
| 1032 | 
            -
                 * @param {number} index - Index in the filtered files list
         | 
| 1033 | 
            -
                 */
         | 
| 1034 | 
            -
                showFileDetailsByIndex(index) {
         | 
| 1035 | 
            -
                    let filesArray = Array.from(this.fileOperations.entries())
         | 
| 1036 | 
            -
                        .filter(([filePath, fileData]) => {
         | 
| 1037 | 
            -
                            return fileData.operations && fileData.operations.length > 0;
         | 
| 1038 | 
            -
                        })
         | 
| 1039 | 
            -
                        .sort((a, b) => {
         | 
| 1040 | 
            -
                            const timeA = a[1].lastOperation ? new Date(a[1].lastOperation) : new Date(0);
         | 
| 1041 | 
            -
                            const timeB = b[1].lastOperation ? new Date(b[1].lastOperation) : new Date(0);
         | 
| 1042 | 
            -
                            return timeA - timeB;
         | 
| 1043 | 
            -
                        });
         | 
| 1044 | 
            -
             | 
| 1045 | 
            -
                    filesArray = this.applyFilesFilters(filesArray);
         | 
| 1046 | 
            -
             | 
| 1047 | 
            -
                    if (index < 0 || index >= filesArray.length) return;
         | 
| 1048 | 
            -
             | 
| 1049 | 
            -
                    const [filePath] = filesArray[index];
         | 
| 1050 | 
            -
                    this.showFileDetails(filePath);
         | 
| 1051 | 
            -
                }
         | 
| 1052 | 
            -
             | 
| 1053 | 
            -
                /**
         | 
| 1054 | 
            -
                 * Render content for the current tab
         | 
| 1055 | 
            -
                 */
         | 
| 1056 | 
            -
                renderCurrentTab() {
         | 
| 1057 | 
            -
                    switch (this.currentTab) {
         | 
| 1058 | 
            -
                        case 'events':
         | 
| 1059 | 
            -
                            // Events are automatically rendered by EventViewer
         | 
| 1060 | 
            -
                            break;
         | 
| 1061 | 
            -
                        case 'agents':
         | 
| 1062 | 
            -
                            this.renderAgents();
         | 
| 1063 | 
            -
                            break;
         | 
| 1064 | 
            -
                        case 'tools':
         | 
| 1065 | 
            -
                            this.renderTools();
         | 
| 1066 | 
            -
                            break;
         | 
| 1067 | 
            -
                        case 'files':
         | 
| 1068 | 
            -
                            this.renderFiles();
         | 
| 1069 | 
            -
                            break;
         | 
| 1070 | 
            -
                    }
         | 
| 1071 | 
            -
                    
         | 
| 1072 | 
            -
                    // Update navigation items for the current tab after rendering
         | 
| 1073 | 
            -
                    this.updateTabNavigationItems();
         | 
| 1074 | 
            -
                    
         | 
| 1075 | 
            -
                    // Restore selection after rendering if it's in the current tab
         | 
| 1076 | 
            -
                    if (this.selectedCard.tab === this.currentTab) {
         | 
| 1077 | 
            -
                        this.updateCardSelectionUI();
         | 
| 1078 | 
            -
                    }
         | 
| 1079 | 
            -
                    
         | 
| 1080 | 
            -
                    // Update unified selection UI to maintain consistency
         | 
| 1081 | 
            -
                    this.updateUnifiedSelectionUI();
         | 
| 1082 | 
            -
                }
         | 
| 1083 | 
            -
             | 
| 1084 | 
            -
                /**
         | 
| 1085 | 
            -
                 * Render agents tab
         | 
| 1086 | 
            -
                 */
         | 
| 1087 | 
            -
                renderAgents() {
         | 
| 1088 | 
            -
                    console.log('=== RENDERAGENTS DEBUG START ===');
         | 
| 1089 | 
            -
                    console.log('1. Function called, checking agentsList element...');
         | 
| 1090 | 
            -
                    
         | 
| 1091 | 
            -
                    const agentsList = document.getElementById('agents-list');
         | 
| 1092 | 
            -
                    if (!agentsList) {
         | 
| 1093 | 
            -
                        console.error('agentsList element not found!');
         | 
| 1094 | 
            -
                        return;
         | 
| 1095 | 
            -
                    }
         | 
| 1096 | 
            -
                    console.log('2. agentsList element found:', agentsList);
         | 
| 1097 | 
            -
             | 
| 1098 | 
            -
                    const events = this.getFilteredEventsForTab('agents');
         | 
| 1099 | 
            -
                    console.log('3. Total events from getFilteredEventsForTab:', events.length);
         | 
| 1100 | 
            -
                    
         | 
| 1101 | 
            -
                    // Enhanced debugging: log first few events to understand structure
         | 
| 1102 | 
            -
                    if (events.length > 0) {
         | 
| 1103 | 
            -
                        console.log('Agent tab - sample events for analysis:');
         | 
| 1104 | 
            -
                        events.slice(0, 3).forEach((event, i) => {
         | 
| 1105 | 
            -
                            console.log(`  Event ${i}:`, {
         | 
| 1106 | 
            -
                                type: event.type,
         | 
| 1107 | 
            -
                                subtype: event.subtype,
         | 
| 1108 | 
            -
                                tool_name: event.tool_name,
         | 
| 1109 | 
            -
                                agent_type: event.agent_type,
         | 
| 1110 | 
            -
                                subagent_type: event.subagent_type,
         | 
| 1111 | 
            -
                                tool_parameters: event.tool_parameters,
         | 
| 1112 | 
            -
                                delegation_details: event.delegation_details,
         | 
| 1113 | 
            -
                                data: event.data ? {
         | 
| 1114 | 
            -
                                    agent_type: event.data.agent_type,
         | 
| 1115 | 
            -
                                    subagent_type: event.data.subagent_type,
         | 
| 1116 | 
            -
                                    event_type: event.data.event_type,
         | 
| 1117 | 
            -
                                    tool_name: event.data.tool_name,
         | 
| 1118 | 
            -
                                    tool_parameters: event.data.tool_parameters,
         | 
| 1119 | 
            -
                                    delegation_details: event.data.delegation_details
         | 
| 1120 | 
            -
                                } : 'no data field'
         | 
| 1121 | 
            -
                            });
         | 
| 1122 | 
            -
                        });
         | 
| 1123 | 
            -
                        
         | 
| 1124 | 
            -
                        // Count events by type and tool_name for debugging
         | 
| 1125 | 
            -
                        const eventCounts = {};
         | 
| 1126 | 
            -
                        const toolCounts = {};
         | 
| 1127 | 
            -
                        const agentCounts = {};
         | 
| 1128 | 
            -
                        events.forEach(event => {
         | 
| 1129 | 
            -
                            const key = `${event.type}.${event.subtype || 'none'}`;
         | 
| 1130 | 
            -
                            eventCounts[key] = (eventCounts[key] || 0) + 1;
         | 
| 1131 | 
            -
                            
         | 
| 1132 | 
            -
                            if (event.tool_name) {
         | 
| 1133 | 
            -
                                toolCounts[event.tool_name] = (toolCounts[event.tool_name] || 0) + 1;
         | 
| 1134 | 
            -
                            }
         | 
| 1135 | 
            -
                            
         | 
| 1136 | 
            -
                            // Count agent types from multiple sources
         | 
| 1137 | 
            -
                            const agentTypes = [];
         | 
| 1138 | 
            -
                            if (event.agent_type) agentTypes.push(`direct:${event.agent_type}`);
         | 
| 1139 | 
            -
                            if (event.subagent_type) agentTypes.push(`sub:${event.subagent_type}`);
         | 
| 1140 | 
            -
                            if (event.tool_parameters?.subagent_type) agentTypes.push(`tool_param:${event.tool_parameters.subagent_type}`);
         | 
| 1141 | 
            -
                            if (event.data?.agent_type) agentTypes.push(`data:${event.data.agent_type}`);
         | 
| 1142 | 
            -
                            if (event.data?.subagent_type) agentTypes.push(`data_sub:${event.data.subagent_type}`);
         | 
| 1143 | 
            -
                            if (event.data?.delegation_details?.agent_type) agentTypes.push(`delegation:${event.data.delegation_details.agent_type}`);
         | 
| 1144 | 
            -
                            
         | 
| 1145 | 
            -
                            agentTypes.forEach(agentType => {
         | 
| 1146 | 
            -
                                agentCounts[agentType] = (agentCounts[agentType] || 0) + 1;
         | 
| 1147 | 
            -
                            });
         | 
| 1148 | 
            -
                        });
         | 
| 1149 | 
            -
                        console.log('Agent tab - event type breakdown:', eventCounts);
         | 
| 1150 | 
            -
                        console.log('Agent tab - tool breakdown:', toolCounts);
         | 
| 1151 | 
            -
                        console.log('Agent tab - agent type breakdown:', agentCounts);
         | 
| 1152 | 
            -
                    }
         | 
| 1153 | 
            -
                    
         | 
| 1154 | 
            -
                    // Use agent inference to filter events instead of hardcoded logic
         | 
| 1155 | 
            -
                    let agentEvents = events
         | 
| 1156 | 
            -
                        .map((event, index) => ({ event, index, inference: this.inferAgentFromEvent(event) }))
         | 
| 1157 | 
            -
                        .filter(({ event, index, inference }) => {
         | 
| 1158 | 
            -
                            // Show events that have meaningful agent context
         | 
| 1159 | 
            -
                            if (!inference) return false;
         | 
| 1160 | 
            -
                            
         | 
| 1161 | 
            -
                            // Include events that are definitely agent-related
         | 
| 1162 | 
            -
                            const isAgentRelated = inference.type === 'subagent' || 
         | 
| 1163 | 
            -
                                                 (inference.type === 'main_agent' && inference.confidence !== 'default') ||
         | 
| 1164 | 
            -
                                                 event.tool_name === 'Task' ||
         | 
| 1165 | 
            -
                                                 event.hook_event_name === 'SubagentStop' ||
         | 
| 1166 | 
            -
                                                 event.subtype === 'subagent_stop';
         | 
| 1167 | 
            -
                            
         | 
| 1168 | 
            -
                            // Debug first few events
         | 
| 1169 | 
            -
                            if (index < 5) {
         | 
| 1170 | 
            -
                                console.log(`Agent filter [${index}] - ${isAgentRelated ? 'MATCHED' : 'SKIPPED'}:`, {
         | 
| 1171 | 
            -
                                    type: event.type,
         | 
| 1172 | 
            -
                                    subtype: event.subtype,
         | 
| 1173 | 
            -
                                    tool_name: event.tool_name,
         | 
| 1174 | 
            -
                                    inference: inference,
         | 
| 1175 | 
            -
                                    isAgentRelated
         | 
| 1176 | 
            -
                                });
         | 
| 1177 | 
            -
                            }
         | 
| 1178 | 
            -
                            
         | 
| 1179 | 
            -
                            return isAgentRelated;
         | 
| 1180 | 
            -
                        })
         | 
| 1181 | 
            -
                        .map(({ event, inference }) => ({ event, inference }))
         | 
| 1182 | 
            -
                        .sort((a, b) => new Date(a.event.timestamp) - new Date(b.event.timestamp));
         | 
| 1183 | 
            -
             | 
| 1184 | 
            -
                    // Extract unique agent types from the data for filter dropdown
         | 
| 1185 | 
            -
                    const uniqueAgentTypes = new Set();
         | 
| 1186 | 
            -
                    agentEvents.forEach(({ event, inference }) => {
         | 
| 1187 | 
            -
                        if (inference && inference.agentName && inference.agentName !== 'Unknown') {
         | 
| 1188 | 
            -
                            uniqueAgentTypes.add(inference.agentName);
         | 
| 1189 | 
            -
                        }
         | 
| 1190 | 
            -
                        // Also check for agent_type in the event data
         | 
| 1191 | 
            -
                        if (event.agent_type && event.agent_type !== 'unknown' && event.agent_type !== 'main') {
         | 
| 1192 | 
            -
                            uniqueAgentTypes.add(event.agent_type);
         | 
| 1193 | 
            -
                        }
         | 
| 1194 | 
            -
                        if (event.subagent_type) {
         | 
| 1195 | 
            -
                            uniqueAgentTypes.add(event.subagent_type);
         | 
| 1196 | 
            -
                        }
         | 
| 1197 | 
            -
                    });
         | 
| 1198 | 
            -
             | 
| 1199 | 
            -
                    // Populate the agents filter dropdown
         | 
| 1200 | 
            -
                    this.populateFilterDropdown('agents-type-filter', Array.from(uniqueAgentTypes), 'All Agents');
         | 
| 1201 | 
            -
             | 
| 1202 | 
            -
                    // Apply tab-specific filters to the agentEvents array while preserving inference data
         | 
| 1203 | 
            -
                    let filteredAgentEvents = agentEvents.filter(({ event }) => {
         | 
| 1204 | 
            -
                        // Create a temporary array with just events for the existing filter function
         | 
| 1205 | 
            -
                        const singleEventArray = [event];
         | 
| 1206 | 
            -
                        const filteredSingleEvent = this.applyAgentsFilters(singleEventArray);
         | 
| 1207 | 
            -
                        return filteredSingleEvent.length > 0;
         | 
| 1208 | 
            -
                    });
         | 
| 1209 | 
            -
                    
         | 
| 1210 | 
            -
                    // Store filtered agent events with inference data in class property
         | 
| 1211 | 
            -
                    this.agentEventsWithInference = filteredAgentEvents;
         | 
| 1212 | 
            -
                    
         | 
| 1213 | 
            -
                    // Also store just the events for backward compatibility
         | 
| 1214 | 
            -
                    this.agentEvents = filteredAgentEvents.map(({ event }) => event);
         | 
| 1215 | 
            -
             | 
| 1216 | 
            -
                    console.log('4. Agent tab - filtering summary:', {
         | 
| 1217 | 
            -
                        total_events: events.length,
         | 
| 1218 | 
            -
                        agent_events_found: filteredAgentEvents.length,
         | 
| 1219 | 
            -
                        percentage: filteredAgentEvents.length > 0 ? ((filteredAgentEvents.length / events.length) * 100).toFixed(1) + '%' : '0%'
         | 
| 1220 | 
            -
                    });
         | 
| 1221 | 
            -
             | 
| 1222 | 
            -
                    if (filteredAgentEvents.length === 0) {
         | 
| 1223 | 
            -
                        console.log('5. No agent events found, showing empty message');
         | 
| 1224 | 
            -
                        agentsList.innerHTML = '<div class="no-events">No agent events found...</div>';
         | 
| 1225 | 
            -
                        return;
         | 
| 1226 | 
            -
                    }
         | 
| 1227 | 
            -
             | 
| 1228 | 
            -
                    console.log('Rendering', filteredAgentEvents.length, 'agent events');
         | 
| 1229 | 
            -
             | 
| 1230 | 
            -
                    const agentsHtml = filteredAgentEvents.map(({ event, inference }, index) => {
         | 
| 1231 | 
            -
                        const timestamp = new Date(event.timestamp).toLocaleTimeString();
         | 
| 1232 | 
            -
                        
         | 
| 1233 | 
            -
                        let agentName = inference ? inference.agentName : 'Unknown';
         | 
| 1234 | 
            -
                        let operation = 'operation';
         | 
| 1235 | 
            -
                        let prompt = '';
         | 
| 1236 | 
            -
                        let description = '';
         | 
| 1237 | 
            -
                        let taskPreview = '';
         | 
| 1238 | 
            -
                        let confidence = inference ? inference.confidence : 'unknown';
         | 
| 1239 | 
            -
                        let reason = inference ? inference.reason : 'no inference';
         | 
| 1240 | 
            -
                        
         | 
| 1241 | 
            -
                        // Extract Task tool information if present
         | 
| 1242 | 
            -
                        const data = event.data || {};
         | 
| 1243 | 
            -
                        if (event.tool_name === 'Task' || data.tool_name === 'Task') {
         | 
| 1244 | 
            -
                            operation = 'delegation';
         | 
| 1245 | 
            -
                            
         | 
| 1246 | 
            -
                            // Try different sources for Task tool data
         | 
| 1247 | 
            -
                            const taskParams = event.tool_parameters || data.tool_parameters || data.delegation_details || {};
         | 
| 1248 | 
            -
                            
         | 
| 1249 | 
            -
                            if (taskParams.prompt) {
         | 
| 1250 | 
            -
                                prompt = taskParams.prompt;
         | 
| 1251 | 
            -
                                taskPreview = prompt.length > 200 ? prompt.substring(0, 200) + '...' : prompt;
         | 
| 1252 | 
            -
                            }
         | 
| 1253 | 
            -
                            if (taskParams.description) {
         | 
| 1254 | 
            -
                                description = taskParams.description;
         | 
| 1255 | 
            -
                            }
         | 
| 1256 | 
            -
                        }
         | 
| 1257 | 
            -
                        
         | 
| 1258 | 
            -
                        // Extract operation from event type/subtype
         | 
| 1259 | 
            -
                        if (event.subtype) {
         | 
| 1260 | 
            -
                            operation = event.subtype.replace(/_/g, ' ');
         | 
| 1261 | 
            -
                        } else {
         | 
| 1262 | 
            -
                            operation = this.extractOperation(event.type) || 'operation';
         | 
| 1263 | 
            -
                        }
         | 
| 1264 | 
            -
                        
         | 
| 1265 | 
            -
                        // Add confidence indicator
         | 
| 1266 | 
            -
                        const confidenceIcon = {
         | 
| 1267 | 
            -
                            'definitive': '๐ฏ',
         | 
| 1268 | 
            -
                            'high': 'โ
',
         | 
| 1269 | 
            -
                            'medium': 'โ ๏ธ',
         | 
| 1270 | 
            -
                            'inherited': '๐',
         | 
| 1271 | 
            -
                            'default': 'โ',
         | 
| 1272 | 
            -
                            'unknown': 'โ'
         | 
| 1273 | 
            -
                        }[confidence] || 'โ';
         | 
| 1274 | 
            -
             | 
| 1275 | 
            -
                        const onclickString = `dashboard.selectCard('agents', ${index}, 'agent', ${index}); dashboard.showAgentDetailsByIndex(${index});`;
         | 
| 1276 | 
            -
             | 
| 1277 | 
            -
                        return `
         | 
| 1278 | 
            -
                            <div class="event-item event-agent" onclick="${onclickString}">
         | 
| 1279 | 
            -
                                <div class="event-header">
         | 
| 1280 | 
            -
                                    <span class="event-type">๐ค ${agentName}</span>
         | 
| 1281 | 
            -
                                    <span class="confidence-indicator" title="Confidence: ${confidence} (${reason})">${confidenceIcon}</span>
         | 
| 1282 | 
            -
                                    <span class="event-timestamp">${timestamp}</span>
         | 
| 1283 | 
            -
                                </div>
         | 
| 1284 | 
            -
                                <div class="event-data">
         | 
| 1285 | 
            -
                                    <strong>Operation:</strong> ${operation}
         | 
| 1286 | 
            -
                                    <strong>Inference:</strong> ${inference ? inference.type : 'unknown'} (${confidence})
         | 
| 1287 | 
            -
                                    ${taskPreview ? `<br><strong>Task Preview:</strong> ${taskPreview}` : ''}
         | 
| 1288 | 
            -
                                    ${description ? `<br><strong>Description:</strong> ${description}` : ''}
         | 
| 1289 | 
            -
                                    ${event.session_id || data.session_id ? `<br><strong>Session:</strong> ${(event.session_id || data.session_id).substring(0, 8)}...` : ''}
         | 
| 1290 | 
            -
                                </div>
         | 
| 1291 | 
            -
                            </div>
         | 
| 1292 | 
            -
                        `;
         | 
| 1293 | 
            -
                    }).join('');
         | 
| 1294 | 
            -
             | 
| 1295 | 
            -
                    console.log('9. Generated HTML length:', agentsHtml.length);
         | 
| 1296 | 
            -
                    console.log('10. Sample HTML (first 500 chars):', agentsHtml.substring(0, 500));
         | 
| 1297 | 
            -
                    
         | 
| 1298 | 
            -
                    agentsList.innerHTML = agentsHtml;
         | 
| 1299 | 
            -
                    
         | 
| 1300 | 
            -
                    // Check if the HTML was actually set
         | 
| 1301 | 
            -
                    console.log('11. HTML set in DOM, innerHTML length:', agentsList.innerHTML.length);
         | 
| 1302 | 
            -
                    console.log('12. Number of event-agent elements:', agentsList.querySelectorAll('.event-agent').length);
         | 
| 1303 | 
            -
                    
         | 
| 1304 | 
            -
                    // Test onclick on first element if exists
         | 
| 1305 | 
            -
                    const firstAgent = agentsList.querySelector('.event-agent');
         | 
| 1306 | 
            -
                    if (firstAgent) {
         | 
| 1307 | 
            -
                        console.log('13. First agent element found:', firstAgent);
         | 
| 1308 | 
            -
                        console.log('14. First agent onclick attribute:', firstAgent.getAttribute('onclick'));
         | 
| 1309 | 
            -
                        
         | 
| 1310 | 
            -
                        // Add a test click event listener as well
         | 
| 1311 | 
            -
                        firstAgent.addEventListener('click', function(e) {
         | 
| 1312 | 
            -
                            console.log('15. CLICK EVENT DETECTED on agent element!', e.target);
         | 
| 1313 | 
            -
                        });
         | 
| 1314 | 
            -
                    } else {
         | 
| 1315 | 
            -
                        console.log('13. No .event-agent elements found in DOM after setting innerHTML');
         | 
| 1316 | 
            -
                    }
         | 
| 1317 | 
            -
                    
         | 
| 1318 | 
            -
                    console.log('=== RENDERAGENTS DEBUG END ===');
         | 
| 1319 | 
            -
                    this.scrollListToBottom('agents-list');
         | 
| 1320 | 
            -
                }
         | 
| 1321 | 
            -
             | 
| 1322 | 
            -
                /**
         | 
| 1323 | 
            -
                 * Render tools tab - shows paired tool calls instead of individual events
         | 
| 1324 | 
            -
                 */
         | 
| 1325 | 
            -
                renderTools() {
         | 
| 1326 | 
            -
                    const toolsList = document.getElementById('tools-list');
         | 
| 1327 | 
            -
                    if (!toolsList) return;
         | 
| 1328 | 
            -
             | 
| 1329 | 
            -
                    console.log('Tools tab - total tool calls:', this.toolCalls.size);
         | 
| 1330 | 
            -
                    
         | 
| 1331 | 
            -
                    if (this.toolCalls.size === 0) {
         | 
| 1332 | 
            -
                        toolsList.innerHTML = '<div class="no-events">No tool calls found...</div>';
         | 
| 1333 | 
            -
                        return;
         | 
| 1334 | 
            -
                    }
         | 
| 1335 | 
            -
             | 
| 1336 | 
            -
                    // Convert to array and sort by timestamp
         | 
| 1337 | 
            -
                    let toolCallsArray = Array.from(this.toolCalls.entries())
         | 
| 1338 | 
            -
                        .filter(([key, toolCall]) => {
         | 
| 1339 | 
            -
                            // Ensure we have valid data
         | 
| 1340 | 
            -
                            return toolCall.tool_name && (toolCall.pre_event || toolCall.post_event);
         | 
| 1341 | 
            -
                        })
         | 
| 1342 | 
            -
                        .sort((a, b) => {
         | 
| 1343 | 
            -
                            const timeA = new Date(a[1].timestamp || 0);
         | 
| 1344 | 
            -
                            const timeB = new Date(b[1].timestamp || 0);
         | 
| 1345 | 
            -
                            return timeA - timeB;
         | 
| 1346 | 
            -
                        });
         | 
| 1347 | 
            -
             | 
| 1348 | 
            -
                    console.log('Tools tab - after filtering:', toolCallsArray.length, 'tool calls');
         | 
| 1349 | 
            -
             | 
| 1350 | 
            -
                    // Extract unique tool names from the data for filter dropdown
         | 
| 1351 | 
            -
                    const uniqueToolNames = new Set();
         | 
| 1352 | 
            -
                    toolCallsArray.forEach(([key, toolCall]) => {
         | 
| 1353 | 
            -
                        if (toolCall.tool_name) {
         | 
| 1354 | 
            -
                            uniqueToolNames.add(toolCall.tool_name);
         | 
| 1355 | 
            -
                        }
         | 
| 1356 | 
            -
                    });
         | 
| 1357 | 
            -
             | 
| 1358 | 
            -
                    // Populate the tools filter dropdown
         | 
| 1359 | 
            -
                    this.populateFilterDropdown('tools-type-filter', Array.from(uniqueToolNames), 'All Tools');
         | 
| 1360 | 
            -
             | 
| 1361 | 
            -
                    // Apply tab-specific filters to tool calls
         | 
| 1362 | 
            -
                    toolCallsArray = this.applyToolCallFilters(toolCallsArray);
         | 
| 1363 | 
            -
             | 
| 1364 | 
            -
                    console.log('Tools tab - after search/type filters:', toolCallsArray.length, 'tool calls');
         | 
| 1365 | 
            -
             | 
| 1366 | 
            -
                    if (toolCallsArray.length === 0) {
         | 
| 1367 | 
            -
                        toolsList.innerHTML = '<div class="no-events">No tool calls match current filters...</div>';
         | 
| 1368 | 
            -
                        return;
         | 
| 1369 | 
            -
                    }
         | 
| 1370 | 
            -
             | 
| 1371 | 
            -
                    const toolsHtml = toolCallsArray.map(([key, toolCall], index) => {
         | 
| 1372 | 
            -
                        const timestamp = new Date(toolCall.timestamp).toLocaleTimeString();
         | 
| 1373 | 
            -
                        const toolName = toolCall.tool_name || 'Unknown Tool';
         | 
| 1374 | 
            -
                        
         | 
| 1375 | 
            -
                        // Use inferred agent data instead of hardcoded 'PM'
         | 
| 1376 | 
            -
                        let agentName = 'PM';
         | 
| 1377 | 
            -
                        let confidence = 'default';
         | 
| 1378 | 
            -
                        
         | 
| 1379 | 
            -
                        // Try to get inference from pre_event first, then post_event
         | 
| 1380 | 
            -
                        const preEvent = toolCall.pre_event;
         | 
| 1381 | 
            -
                        const postEvent = toolCall.post_event;
         | 
| 1382 | 
            -
                        
         | 
| 1383 | 
            -
                        if (preEvent) {
         | 
| 1384 | 
            -
                            const eventIndex = this.eventViewer.events.indexOf(preEvent);
         | 
| 1385 | 
            -
                            const inference = this.getInferredAgent(eventIndex);
         | 
| 1386 | 
            -
                            if (inference) {
         | 
| 1387 | 
            -
                                agentName = inference.agentName;
         | 
| 1388 | 
            -
                                confidence = inference.confidence;
         | 
| 1389 | 
            -
                            }
         | 
| 1390 | 
            -
                        } else if (postEvent) {
         | 
| 1391 | 
            -
                            const eventIndex = this.eventViewer.events.indexOf(postEvent);
         | 
| 1392 | 
            -
                            const inference = this.getInferredAgent(eventIndex);
         | 
| 1393 | 
            -
                            if (inference) {
         | 
| 1394 | 
            -
                                agentName = inference.agentName;
         | 
| 1395 | 
            -
                                confidence = inference.confidence;
         | 
| 1396 | 
            -
                            }
         | 
| 1397 | 
            -
                        }
         | 
| 1398 | 
            -
                        
         | 
| 1399 | 
            -
                        // Fallback to existing logic if no inference available
         | 
| 1400 | 
            -
                        if (agentName === 'PM' && confidence === 'default') {
         | 
| 1401 | 
            -
                            agentName = toolCall.agent_type || 'PM';
         | 
| 1402 | 
            -
                        }
         | 
| 1403 | 
            -
                        
         | 
| 1404 | 
            -
                        // Extract tool target/parameters from pre_event
         | 
| 1405 | 
            -
                        const target = preEvent ? this.extractToolTarget(toolName, preEvent.tool_parameters, preEvent.tool_parameters) : 'Unknown target';
         | 
| 1406 | 
            -
                        
         | 
| 1407 | 
            -
                        // Determine status and duration
         | 
| 1408 | 
            -
                        let statusInfo = '';
         | 
| 1409 | 
            -
                        let statusClass = '';
         | 
| 1410 | 
            -
                        
         | 
| 1411 | 
            -
                        if (toolCall.post_event) {
         | 
| 1412 | 
            -
                            // We have completion data
         | 
| 1413 | 
            -
                            const duration = toolCall.duration_ms ? `${toolCall.duration_ms}ms` : 'Unknown duration';
         | 
| 1414 | 
            -
                            const success = toolCall.success !== undefined ? toolCall.success : 'Unknown';
         | 
| 1415 | 
            -
                            
         | 
| 1416 | 
            -
                            if (success === true) {
         | 
| 1417 | 
            -
                                statusInfo = `โ
 Success (${duration})`;
         | 
| 1418 | 
            -
                                statusClass = 'tool-success';
         | 
| 1419 | 
            -
                            } else if (success === false) {
         | 
| 1420 | 
            -
                                statusInfo = `โ Failed (${duration})`;
         | 
| 1421 | 
            -
                                statusClass = 'tool-failure';
         | 
| 1422 | 
            -
                            } else {
         | 
| 1423 | 
            -
                                statusInfo = `โณ Completed (${duration})`;
         | 
| 1424 | 
            -
                                statusClass = 'tool-completed';
         | 
| 1425 | 
            -
                            }
         | 
| 1426 | 
            -
                        } else {
         | 
| 1427 | 
            -
                            // Only pre_event - still running or incomplete
         | 
| 1428 | 
            -
                            statusInfo = 'โณ Running...';
         | 
| 1429 | 
            -
                            statusClass = 'tool-running';
         | 
| 1430 | 
            -
                        }
         | 
| 1431 | 
            -
                        
         | 
| 1432 | 
            -
                        // Add confidence indicator for agent inference
         | 
| 1433 | 
            -
                        const confidenceIcon = {
         | 
| 1434 | 
            -
                            'definitive': '๐ฏ',
         | 
| 1435 | 
            -
                            'high': 'โ
',
         | 
| 1436 | 
            -
                            'medium': 'โ ๏ธ',
         | 
| 1437 | 
            -
                            'inherited': '๐',
         | 
| 1438 | 
            -
                            'default': 'โ',
         | 
| 1439 | 
            -
                            'unknown': 'โ'
         | 
| 1440 | 
            -
                        }[confidence] || 'โ';
         | 
| 1441 | 
            -
                        
         | 
| 1442 | 
            -
                        return `
         | 
| 1443 | 
            -
                            <div class="event-item event-tool ${statusClass}" onclick="dashboard.selectCard('tools', ${index}, 'toolCall', '${key}'); dashboard.showToolCallDetails('${key}')">
         | 
| 1444 | 
            -
                                <div class="event-header">
         | 
| 1445 | 
            -
                                    <span class="event-type">๐ง ${toolName}</span>
         | 
| 1446 | 
            -
                                    <span class="event-timestamp">${timestamp}</span>
         | 
| 1447 | 
            -
                                </div>
         | 
| 1448 | 
            -
                                <div class="event-data">
         | 
| 1449 | 
            -
                                    <strong>Agent:</strong> ${agentName} (${confidence})<br>
         | 
| 1450 | 
            -
                                    <strong>Status:</strong> ${statusInfo}<br>
         | 
| 1451 | 
            -
                                    <strong>Target:</strong> ${target}
         | 
| 1452 | 
            -
                                    ${toolCall.session_id ? `<br><strong>Session:</strong> ${toolCall.session_id.substring(0, 8)}...` : ''}
         | 
| 1453 | 
            -
                                </div>
         | 
| 1454 | 
            -
                            </div>
         | 
| 1455 | 
            -
                        `;
         | 
| 1456 | 
            -
                    }).join('');
         | 
| 1457 | 
            -
             | 
| 1458 | 
            -
                    toolsList.innerHTML = toolsHtml;
         | 
| 1459 | 
            -
                    this.scrollListToBottom('tools-list');
         | 
| 1460 | 
            -
                }
         | 
| 1461 | 
            -
             | 
| 1462 | 
            -
                /**
         | 
| 1463 | 
            -
                 * Render files tab with file-centric view
         | 
| 1464 | 
            -
                 */
         | 
| 1465 | 
            -
                renderFiles() {
         | 
| 1466 | 
            -
                    const filesList = document.getElementById('files-list');
         | 
| 1467 | 
            -
                    if (!filesList) return;
         | 
| 1468 | 
            -
             | 
| 1469 | 
            -
                    console.log('Files tab - file operations:', this.fileOperations.size);
         | 
| 1470 | 
            -
                    console.log('Files tab - operations map:', this.fileOperations);
         | 
| 1471 | 
            -
             | 
| 1472 | 
            -
                    if (this.fileOperations.size === 0) {
         | 
| 1473 | 
            -
                        filesList.innerHTML = '<div class="no-events">No file operations found...</div>';
         | 
| 1474 | 
            -
                        return;
         | 
| 1475 | 
            -
                    }
         | 
| 1476 | 
            -
             | 
| 1477 | 
            -
                    // Convert to array and sort by most recent operations at bottom (chronological order)
         | 
| 1478 | 
            -
                    let filesArray = Array.from(this.fileOperations.entries())
         | 
| 1479 | 
            -
                        .filter(([filePath, fileData]) => {
         | 
| 1480 | 
            -
                            // Ensure we have valid data
         | 
| 1481 | 
            -
                            return fileData.operations && fileData.operations.length > 0;
         | 
| 1482 | 
            -
                        })
         | 
| 1483 | 
            -
                        .sort((a, b) => {
         | 
| 1484 | 
            -
                            const timeA = a[1].lastOperation ? new Date(a[1].lastOperation) : new Date(0);
         | 
| 1485 | 
            -
                            const timeB = b[1].lastOperation ? new Date(b[1].lastOperation) : new Date(0);
         | 
| 1486 | 
            -
                            return timeA - timeB;
         | 
| 1487 | 
            -
                        });
         | 
| 1488 | 
            -
             | 
| 1489 | 
            -
                    console.log('Files tab - after filtering:', filesArray.length, 'files');
         | 
| 1490 | 
            -
             | 
| 1491 | 
            -
                    // Extract unique operations from the data for filter dropdown
         | 
| 1492 | 
            -
                    const uniqueOperations = new Set();
         | 
| 1493 | 
            -
                    filesArray.forEach(([filePath, fileData]) => {
         | 
| 1494 | 
            -
                        if (fileData.operations && fileData.operations.length > 0) {
         | 
| 1495 | 
            -
                            fileData.operations.forEach(operation => {
         | 
| 1496 | 
            -
                                if (operation.operation) {
         | 
| 1497 | 
            -
                                    uniqueOperations.add(operation.operation);
         | 
| 1498 | 
            -
                                }
         | 
| 1499 | 
            -
                            });
         | 
| 1500 | 
            -
                        }
         | 
| 1501 | 
            -
                    });
         | 
| 1502 | 
            -
             | 
| 1503 | 
            -
                    // Populate the files filter dropdown
         | 
| 1504 | 
            -
                    this.populateFilterDropdown('files-type-filter', Array.from(uniqueOperations), 'All Operations');
         | 
| 1505 | 
            -
             | 
| 1506 | 
            -
                    // Apply tab-specific filters
         | 
| 1507 | 
            -
                    filesArray = this.applyFilesFilters(filesArray);
         | 
| 1508 | 
            -
             | 
| 1509 | 
            -
                    console.log('Files tab - after search/type filters:', filesArray.length, 'files');
         | 
| 1510 | 
            -
             | 
| 1511 | 
            -
                    if (filesArray.length === 0) {
         | 
| 1512 | 
            -
                        filesList.innerHTML = '<div class="no-events">No files match current filters...</div>';
         | 
| 1513 | 
            -
                        return;
         | 
| 1514 | 
            -
                    }
         | 
| 1515 | 
            -
             | 
| 1516 | 
            -
                    const filesHtml = filesArray.map(([filePath, fileData], index) => {
         | 
| 1517 | 
            -
                        if (!fileData.operations || fileData.operations.length === 0) {
         | 
| 1518 | 
            -
                            console.warn('File with no operations:', filePath);
         | 
| 1519 | 
            -
                            return '';
         | 
| 1520 | 
            -
                        }
         | 
| 1521 | 
            -
                        
         | 
| 1522 | 
            -
                        const icon = this.getFileOperationIcon(fileData.operations);
         | 
| 1523 | 
            -
                        const lastOp = fileData.operations[fileData.operations.length - 1];
         | 
| 1524 | 
            -
                        const timestamp = new Date(lastOp.timestamp).toLocaleTimeString();
         | 
| 1525 | 
            -
                        
         | 
| 1526 | 
            -
                        // Get unique operations as text, joined with |
         | 
| 1527 | 
            -
                        const uniqueOperations = [...new Set(fileData.operations.map(op => op.operation))];
         | 
| 1528 | 
            -
                        const operationsText = uniqueOperations.join('|');
         | 
| 1529 | 
            -
                        
         | 
| 1530 | 
            -
                        return `
         | 
| 1531 | 
            -
                            <div class="event-item file-item" onclick="dashboard.selectCard('files', ${index}, 'file', '${filePath}'); dashboard.showFileDetails('${filePath}')">
         | 
| 1532 | 
            -
                                <div class="event-header">
         | 
| 1533 | 
            -
                                    <span class="event-type">${icon}</span>
         | 
| 1534 | 
            -
                                    <span class="file-path">${this.getRelativeFilePath(filePath)}</span>
         | 
| 1535 | 
            -
                                    <span class="event-timestamp">${timestamp}</span>
         | 
| 1536 | 
            -
                                </div>
         | 
| 1537 | 
            -
                                <div class="event-data">
         | 
| 1538 | 
            -
                                    <strong>Operations:</strong> ${operationsText}<br>
         | 
| 1539 | 
            -
                                    <strong>Agent:</strong> ${lastOp.agent} ${lastOp.confidence ? `(${lastOp.confidence})` : ''}
         | 
| 1540 | 
            -
                                </div>
         | 
| 1541 | 
            -
                            </div>
         | 
| 1542 | 
            -
                        `;
         | 
| 1543 | 
            -
                    }).join('');
         | 
| 1544 | 
            -
             | 
| 1545 | 
            -
                    filesList.innerHTML = filesHtml;
         | 
| 1546 | 
            -
                    this.scrollListToBottom('files-list');
         | 
| 1547 | 
            -
                }
         | 
| 1548 | 
            -
             | 
| 1549 | 
            -
                /**
         | 
| 1550 | 
            -
                 * Show agent details in module viewer
         | 
| 1551 | 
            -
                 */
         | 
| 1552 | 
            -
                showAgentDetails(agentIndex, eventIndex) {
         | 
| 1553 | 
            -
                    console.log('showAgentDetails called with agentIndex:', agentIndex, 'eventIndex:', eventIndex);
         | 
| 1554 | 
            -
                    
         | 
| 1555 | 
            -
                    // Use stored filtered agent events with inference data if available
         | 
| 1556 | 
            -
                    const agentEventsWithInference = this.agentEventsWithInference || [];
         | 
| 1557 | 
            -
                    const agentEvents = this.agentEvents;
         | 
| 1558 | 
            -
                    
         | 
| 1559 | 
            -
                    let event, inference;
         | 
| 1560 | 
            -
                    
         | 
| 1561 | 
            -
                    // Try to get event and inference data together
         | 
| 1562 | 
            -
                    if (agentEventsWithInference[agentIndex]) {
         | 
| 1563 | 
            -
                        event = agentEventsWithInference[agentIndex].event;
         | 
| 1564 | 
            -
                        inference = agentEventsWithInference[agentIndex].inference;
         | 
| 1565 | 
            -
                    } else if (agentEvents[agentIndex]) {
         | 
| 1566 | 
            -
                        // Fallback to just event data
         | 
| 1567 | 
            -
                        event = agentEvents[agentIndex];
         | 
| 1568 | 
            -
                        inference = null;
         | 
| 1569 | 
            -
                    } else {
         | 
| 1570 | 
            -
                        return;
         | 
| 1571 | 
            -
                    }
         | 
| 1572 | 
            -
                    
         | 
| 1573 | 
            -
                    // Extract agent information using inference data first, then fallback to event data
         | 
| 1574 | 
            -
                    let agentName = 'Unknown Agent';
         | 
| 1575 | 
            -
                    let prompt = '';
         | 
| 1576 | 
            -
                    let description = '';
         | 
| 1577 | 
            -
                    let fullPrompt = '';
         | 
| 1578 | 
            -
                    
         | 
| 1579 | 
            -
                    // Use inference data for agent name if available
         | 
| 1580 | 
            -
                    if (inference && inference.agentName && inference.agentName !== 'Unknown') {
         | 
| 1581 | 
            -
                        agentName = inference.agentName;
         | 
| 1582 | 
            -
                    } else if (event.tool_name === 'Task' && event.tool_parameters?.subagent_type) {
         | 
| 1583 | 
            -
                        agentName = event.tool_parameters.subagent_type;
         | 
| 1584 | 
            -
                    } else if (event.subagent_type) {
         | 
| 1585 | 
            -
                        agentName = event.subagent_type;
         | 
| 1586 | 
            -
                    } else if (event.agent_type && event.agent_type !== 'unknown') {
         | 
| 1587 | 
            -
                        agentName = event.agent_type;
         | 
| 1588 | 
            -
                    }
         | 
| 1589 | 
            -
                    
         | 
| 1590 | 
            -
                    // Extract task information
         | 
| 1591 | 
            -
                    if (event.tool_name === 'Task' && event.tool_parameters) {
         | 
| 1592 | 
            -
                        prompt = event.tool_parameters.prompt || '';
         | 
| 1593 | 
            -
                        description = event.tool_parameters.description || '';
         | 
| 1594 | 
            -
                        fullPrompt = prompt;
         | 
| 1595 | 
            -
                    }
         | 
| 1596 | 
            -
             | 
| 1597 | 
            -
                    // Add debug logging
         | 
| 1598 | 
            -
                    console.log('showAgentDetails called with:', { 
         | 
| 1599 | 
            -
                        agentIndex, 
         | 
| 1600 | 
            -
                        eventIndex, 
         | 
| 1601 | 
            -
                        event, 
         | 
| 1602 | 
            -
                        inference, 
         | 
| 1603 | 
            -
                        agentName: agentName,
         | 
| 1604 | 
            -
                        hasInferenceData: !!inference 
         | 
| 1605 | 
            -
                    });
         | 
| 1606 | 
            -
                    console.log('moduleViewer available:', !!this.moduleViewer);
         | 
| 1607 | 
            -
             | 
| 1608 | 
            -
                    // Create enhanced event object with inference data for module viewer
         | 
| 1609 | 
            -
                    const enhancedEvent = {
         | 
| 1610 | 
            -
                        ...event,
         | 
| 1611 | 
            -
                        _inference: inference, // Add inference data as a private property
         | 
| 1612 | 
            -
                        _agentName: agentName  // Add resolved agent name
         | 
| 1613 | 
            -
                    };
         | 
| 1614 | 
            -
             | 
| 1615 | 
            -
                    // Use the module viewer's ingest method to properly display the agent event
         | 
| 1616 | 
            -
                    if (this.moduleViewer) {
         | 
| 1617 | 
            -
                        console.log('Calling moduleViewer.ingest with enhanced event:', enhancedEvent);
         | 
| 1618 | 
            -
                        this.moduleViewer.ingest(enhancedEvent);
         | 
| 1619 | 
            -
                    }
         | 
| 1620 | 
            -
                    
         | 
| 1621 | 
            -
                    // Also show the event details in EventViewer
         | 
| 1622 | 
            -
                    if (eventIndex >= 0) {
         | 
| 1623 | 
            -
                        this.eventViewer.showEventDetails(eventIndex);
         | 
| 1624 | 
            -
                    }
         | 
| 1625 | 
            -
                }
         | 
| 1626 | 
            -
             | 
| 1627 | 
            -
                /**
         | 
| 1628 | 
            -
                 * Toggle prompt expansion
         | 
| 1629 | 
            -
                 */
         | 
| 1630 | 
            -
                togglePromptExpansion(button) {
         | 
| 1631 | 
            -
                    const promptDiv = button.parentElement.previousElementSibling;
         | 
| 1632 | 
            -
                    const isExpanded = promptDiv.style.maxHeight !== '300px';
         | 
| 1633 | 
            -
                    
         | 
| 1634 | 
            -
                    if (isExpanded) {
         | 
| 1635 | 
            -
                        promptDiv.style.maxHeight = '300px';
         | 
| 1636 | 
            -
                        button.textContent = 'Show More';
         | 
| 1637 | 
            -
                    } else {
         | 
| 1638 | 
            -
                        promptDiv.style.maxHeight = 'none';
         | 
| 1639 | 
            -
                        button.textContent = 'Show Less';
         | 
| 1640 | 
            -
                    }
         | 
| 1641 | 
            -
                }
         | 
| 1642 | 
            -
             | 
| 1643 | 
            -
                /**
         | 
| 1644 | 
            -
                 * Show tool details in module viewer
         | 
| 1645 | 
            -
                 */
         | 
| 1646 | 
            -
                showToolDetails(toolIndex, eventIndex) {
         | 
| 1647 | 
            -
                    // Get the tool event
         | 
| 1648 | 
            -
                    const events = this.getFilteredEventsForTab('tools');
         | 
| 1649 | 
            -
                    const toolEvents = this.applyToolsFilters(events.filter(event => {
         | 
| 1650 | 
            -
                        const type = event.type || '';
         | 
| 1651 | 
            -
                        const subtype = event.subtype || '';
         | 
| 1652 | 
            -
                        
         | 
| 1653 | 
            -
                        const isHookToolEvent = type === 'hook' && (
         | 
| 1654 | 
            -
                            subtype.includes('tool') || 
         | 
| 1655 | 
            -
                            subtype.includes('pre_') || 
         | 
| 1656 | 
            -
                            subtype.includes('post_')
         | 
| 1657 | 
            -
                        );
         | 
| 1658 | 
            -
                        const hasToolName = event.tool_name;
         | 
| 1659 | 
            -
                        const hasToolsArray = event.tools && Array.isArray(event.tools);
         | 
| 1660 | 
            -
                        const isLegacyHookEvent = type.startsWith('hook.') && (
         | 
| 1661 | 
            -
                            type.includes('tool') || 
         | 
| 1662 | 
            -
                            type.includes('pre') || 
         | 
| 1663 | 
            -
                            type.includes('post')
         | 
| 1664 | 
            -
                        );
         | 
| 1665 | 
            -
                        
         | 
| 1666 | 
            -
                        return isHookToolEvent || hasToolName || hasToolsArray || isLegacyHookEvent;
         | 
| 1667 | 
            -
                    }));
         | 
| 1668 | 
            -
             | 
| 1669 | 
            -
                    const event = toolEvents[toolIndex];
         | 
| 1670 | 
            -
                    if (!event) return;
         | 
| 1671 | 
            -
             | 
| 1672 | 
            -
                    // Extract tool information
         | 
| 1673 | 
            -
                    let toolName = event.tool_name || 'Unknown Tool';
         | 
| 1674 | 
            -
                    if (event.tools && Array.isArray(event.tools) && event.tools.length > 0) {
         | 
| 1675 | 
            -
                        toolName = event.tools[0];
         | 
| 1676 | 
            -
                    }
         | 
| 1677 | 
            -
                    
         | 
| 1678 | 
            -
                    let agentName = 'PM';
         | 
| 1679 | 
            -
                    if (event.subagent_type) {
         | 
| 1680 | 
            -
                        agentName = event.subagent_type;
         | 
| 1681 | 
            -
                    } else if (event.agent_type && event.agent_type !== 'main' && event.agent_type !== 'unknown') {
         | 
| 1682 | 
            -
                        agentName = event.agent_type;
         | 
| 1683 | 
            -
                    }
         | 
| 1684 | 
            -
             | 
| 1685 | 
            -
                    const target = this.extractToolTarget(toolName, event.tool_parameters, event.tool_parameters);
         | 
| 1686 | 
            -
                    
         | 
| 1687 | 
            -
                    let operation = 'execution';
         | 
| 1688 | 
            -
                    if (event.subtype) {
         | 
| 1689 | 
            -
                        if (event.subtype.includes('pre_')) {
         | 
| 1690 | 
            -
                            operation = 'pre-execution';
         | 
| 1691 | 
            -
                        } else if (event.subtype.includes('post_')) {
         | 
| 1692 | 
            -
                            operation = 'post-execution';
         | 
| 1693 | 
            -
                        } else {
         | 
| 1694 | 
            -
                            operation = event.subtype.replace(/_/g, ' ');
         | 
| 1695 | 
            -
                        }
         | 
| 1696 | 
            -
                    }
         | 
| 1697 | 
            -
             | 
| 1698 | 
            -
                    const content = `
         | 
| 1699 | 
            -
                        <div class="structured-view-section">
         | 
| 1700 | 
            -
                            <div class="structured-view-header">
         | 
| 1701 | 
            -
                                <h4>๐ง Tool Details</h4>
         | 
| 1702 | 
            -
                            </div>
         | 
| 1703 | 
            -
                            <div class="tool-details">
         | 
| 1704 | 
            -
                                <div class="tool-info">
         | 
| 1705 | 
            -
                                    <div class="structured-field">
         | 
| 1706 | 
            -
                                        <strong>Tool Name:</strong> ${toolName}
         | 
| 1707 | 
            -
                                    </div>
         | 
| 1708 | 
            -
                                    <div class="structured-field">
         | 
| 1709 | 
            -
                                        <strong>Agent:</strong> ${agentName}
         | 
| 1710 | 
            -
                                    </div>
         | 
| 1711 | 
            -
                                    <div class="structured-field">
         | 
| 1712 | 
            -
                                        <strong>Operation:</strong> ${operation}
         | 
| 1713 | 
            -
                                    </div>
         | 
| 1714 | 
            -
                                    <div class="structured-field">
         | 
| 1715 | 
            -
                                        <strong>Target:</strong> ${target}
         | 
| 1716 | 
            -
                                    </div>
         | 
| 1717 | 
            -
                                    <div class="structured-field">
         | 
| 1718 | 
            -
                                        <strong>Timestamp:</strong> ${new Date(event.timestamp).toLocaleString()}
         | 
| 1719 | 
            -
                                    </div>
         | 
| 1720 | 
            -
                                    <div class="structured-field">
         | 
| 1721 | 
            -
                                        <strong>Event Type:</strong> ${event.type}.${event.subtype || 'default'}
         | 
| 1722 | 
            -
                                    </div>
         | 
| 1723 | 
            -
                                    ${event.session_id ? `
         | 
| 1724 | 
            -
                                        <div class="structured-field">
         | 
| 1725 | 
            -
                                            <strong>Session ID:</strong> ${event.session_id}
         | 
| 1726 | 
            -
                                        </div>
         | 
| 1727 | 
            -
                                    ` : ''}
         | 
| 1728 | 
            -
                                </div>
         | 
| 1729 | 
            -
                                
         | 
| 1730 | 
            -
                                ${event.tool_parameters ? `
         | 
| 1731 | 
            -
                                    <div class="parameters-section">
         | 
| 1732 | 
            -
                                        <div class="structured-view-header">
         | 
| 1733 | 
            -
                                            <h4>โ๏ธ Parameters</h4>
         | 
| 1734 | 
            -
                                        </div>
         | 
| 1735 | 
            -
                                        <div class="structured-data">
         | 
| 1736 | 
            -
                                            <pre style="white-space: pre-wrap; font-family: monospace; font-size: 12px; line-height: 1.4;">${JSON.stringify(event.tool_parameters, null, 2)}</pre>
         | 
| 1737 | 
            -
                                        </div>
         | 
| 1738 | 
            -
                                    </div>
         | 
| 1739 | 
            -
                                ` : ''}
         | 
| 1740 | 
            -
                            </div>
         | 
| 1741 | 
            -
                        </div>
         | 
| 1742 | 
            -
                    `;
         | 
| 1743 | 
            -
             | 
| 1744 | 
            -
                    // Use the new dual-pane approach for tools
         | 
| 1745 | 
            -
                    if (this.moduleViewer.dataContainer) {
         | 
| 1746 | 
            -
                        this.moduleViewer.dataContainer.innerHTML = content;
         | 
| 1747 | 
            -
                    }
         | 
| 1748 | 
            -
                    if (this.moduleViewer.jsonContainer && event) {
         | 
| 1749 | 
            -
                        this.moduleViewer.jsonContainer.innerHTML = `<pre>${JSON.stringify(event, null, 2)}</pre>`;
         | 
| 1750 | 
            -
                    }
         | 
| 1751 | 
            -
                    
         | 
| 1752 | 
            -
                    // Also show the event details in EventViewer
         | 
| 1753 | 
            -
                    if (eventIndex >= 0) {
         | 
| 1754 | 
            -
                        this.eventViewer.showEventDetails(eventIndex);
         | 
| 1755 | 
            -
                    }
         | 
| 1756 | 
            -
                }
         | 
| 1757 | 
            -
             | 
| 1758 | 
            -
                /**
         | 
| 1759 | 
            -
                 * Show tool call details in module viewer with combined pre/post data
         | 
| 1760 | 
            -
                 */
         | 
| 1761 | 
            -
                showToolCallDetails(toolCallKey) {
         | 
| 1762 | 
            -
                    const toolCall = this.toolCalls.get(toolCallKey);
         | 
| 1763 | 
            -
                    if (!toolCall) return;
         | 
| 1764 | 
            -
             | 
| 1765 | 
            -
                    const toolName = toolCall.tool_name || 'Unknown Tool';
         | 
| 1766 | 
            -
                    const agentName = toolCall.agent_type || 'PM';
         | 
| 1767 | 
            -
                    const timestamp = new Date(toolCall.timestamp).toLocaleString();
         | 
| 1768 | 
            -
             | 
| 1769 | 
            -
                    // Extract information from pre and post events
         | 
| 1770 | 
            -
                    const preEvent = toolCall.pre_event;
         | 
| 1771 | 
            -
                    const postEvent = toolCall.post_event;
         | 
| 1772 | 
            -
                    
         | 
| 1773 | 
            -
                    // Get parameters from pre-event
         | 
| 1774 | 
            -
                    const parameters = preEvent?.tool_parameters || {};
         | 
| 1775 | 
            -
                    const target = preEvent ? this.extractToolTarget(toolName, parameters, parameters) : 'Unknown target';
         | 
| 1776 | 
            -
                    
         | 
| 1777 | 
            -
                    // Get execution results from post-event
         | 
| 1778 | 
            -
                    const duration = toolCall.duration_ms ? `${toolCall.duration_ms}ms` : '-';
         | 
| 1779 | 
            -
                    const success = toolCall.success !== undefined ? toolCall.success : null;
         | 
| 1780 | 
            -
                    const exitCode = toolCall.exit_code !== undefined ? toolCall.exit_code : null;
         | 
| 1781 | 
            -
                    // Format result summary properly if it's an object
         | 
| 1782 | 
            -
                    let resultSummary = toolCall.result_summary || 'No summary available';
         | 
| 1783 | 
            -
                    let formattedResultSummary = '';
         | 
| 1784 | 
            -
                    
         | 
| 1785 | 
            -
                    if (typeof resultSummary === 'object' && resultSummary !== null) {
         | 
| 1786 | 
            -
                        // Format the result summary object into human-readable text
         | 
| 1787 | 
            -
                        const parts = [];
         | 
| 1788 | 
            -
                        
         | 
| 1789 | 
            -
                        if (resultSummary.exit_code !== undefined) {
         | 
| 1790 | 
            -
                            parts.push(`Exit Code: ${resultSummary.exit_code}`);
         | 
| 1791 | 
            -
                        }
         | 
| 1792 | 
            -
                        
         | 
| 1793 | 
            -
                        if (resultSummary.has_output !== undefined) {
         | 
| 1794 | 
            -
                            parts.push(`Has Output: ${resultSummary.has_output ? 'Yes' : 'No'}`);
         | 
| 1795 | 
            -
                        }
         | 
| 1796 | 
            -
                        
         | 
| 1797 | 
            -
                        if (resultSummary.has_error !== undefined) {
         | 
| 1798 | 
            -
                            parts.push(`Has Error: ${resultSummary.has_error ? 'Yes' : 'No'}`);
         | 
| 1799 | 
            -
                        }
         | 
| 1800 | 
            -
                        
         | 
| 1801 | 
            -
                        if (resultSummary.output_lines !== undefined) {
         | 
| 1802 | 
            -
                            parts.push(`Output Lines: ${resultSummary.output_lines}`);
         | 
| 1803 | 
            -
                        }
         | 
| 1804 | 
            -
                        
         | 
| 1805 | 
            -
                        if (resultSummary.output_preview) {
         | 
| 1806 | 
            -
                            parts.push(`Output Preview: ${resultSummary.output_preview}`);
         | 
| 1807 | 
            -
                        }
         | 
| 1808 | 
            -
                        
         | 
| 1809 | 
            -
                        if (resultSummary.error_preview) {
         | 
| 1810 | 
            -
                            parts.push(`Error Preview: ${resultSummary.error_preview}`);
         | 
| 1811 | 
            -
                        }
         | 
| 1812 | 
            -
                        
         | 
| 1813 | 
            -
                        formattedResultSummary = parts.join('\n');
         | 
| 1814 | 
            -
                    } else {
         | 
| 1815 | 
            -
                        formattedResultSummary = String(resultSummary);
         | 
| 1816 | 
            -
                    }
         | 
| 1817 | 
            -
             | 
| 1818 | 
            -
                    // Status information
         | 
| 1819 | 
            -
                    let statusIcon = 'โณ';
         | 
| 1820 | 
            -
                    let statusText = 'Running...';
         | 
| 1821 | 
            -
                    let statusClass = 'tool-running';
         | 
| 1822 | 
            -
                    
         | 
| 1823 | 
            -
                    if (postEvent) {
         | 
| 1824 | 
            -
                        if (success === true) {
         | 
| 1825 | 
            -
                            statusIcon = 'โ
';
         | 
| 1826 | 
            -
                            statusText = 'Success';
         | 
| 1827 | 
            -
                            statusClass = 'tool-success';
         | 
| 1828 | 
            -
                        } else if (success === false) {
         | 
| 1829 | 
            -
                            statusIcon = 'โ';
         | 
| 1830 | 
            -
                            statusText = 'Failed';
         | 
| 1831 | 
            -
                            statusClass = 'tool-failure';
         | 
| 1832 | 
            -
                        } else {
         | 
| 1833 | 
            -
                            statusIcon = 'โณ';
         | 
| 1834 | 
            -
                            statusText = 'Completed';
         | 
| 1835 | 
            -
                            statusClass = 'tool-completed';
         | 
| 1836 | 
            -
                        }
         | 
| 1837 | 
            -
                    }
         | 
| 1838 | 
            -
             | 
| 1839 | 
            -
                    const content = `
         | 
| 1840 | 
            -
                        <div class="structured-view-section">
         | 
| 1841 | 
            -
                            <div class="structured-view-header">
         | 
| 1842 | 
            -
                                <h4>๐ง Tool Call Details</h4>
         | 
| 1843 | 
            -
                            </div>
         | 
| 1844 | 
            -
                            <div class="tool-call-details">
         | 
| 1845 | 
            -
                                <div class="tool-call-info ${statusClass}">
         | 
| 1846 | 
            -
                                    <div class="structured-field">
         | 
| 1847 | 
            -
                                        <strong>Tool Name:</strong> ${toolName}
         | 
| 1848 | 
            -
                                    </div>
         | 
| 1849 | 
            -
                                    <div class="structured-field">
         | 
| 1850 | 
            -
                                        <strong>Agent:</strong> ${agentName}
         | 
| 1851 | 
            -
                                    </div>
         | 
| 1852 | 
            -
                                    <div class="structured-field">
         | 
| 1853 | 
            -
                                        <strong>Status:</strong> ${statusIcon} ${statusText}
         | 
| 1854 | 
            -
                                    </div>
         | 
| 1855 | 
            -
                                    <div class="structured-field">
         | 
| 1856 | 
            -
                                        <strong>Target:</strong> ${target}
         | 
| 1857 | 
            -
                                    </div>
         | 
| 1858 | 
            -
                                    <div class="structured-field">
         | 
| 1859 | 
            -
                                        <strong>Started:</strong> ${timestamp}
         | 
| 1860 | 
            -
                                    </div>
         | 
| 1861 | 
            -
                                    <div class="structured-field">
         | 
| 1862 | 
            -
                                        <strong>Duration:</strong> ${duration}
         | 
| 1863 | 
            -
                                    </div>
         | 
| 1864 | 
            -
                                    ${success !== null ? `
         | 
| 1865 | 
            -
                                        <div class="structured-field">
         | 
| 1866 | 
            -
                                            <strong>Success:</strong> ${success}
         | 
| 1867 | 
            -
                                        </div>
         | 
| 1868 | 
            -
                                    ` : ''}
         | 
| 1869 | 
            -
                                    ${exitCode !== null ? `
         | 
| 1870 | 
            -
                                        <div class="structured-field">
         | 
| 1871 | 
            -
                                            <strong>Exit Code:</strong> ${exitCode}
         | 
| 1872 | 
            -
                                        </div>
         | 
| 1873 | 
            -
                                    ` : ''}
         | 
| 1874 | 
            -
                                    ${toolCall.session_id ? `
         | 
| 1875 | 
            -
                                        <div class="structured-field">
         | 
| 1876 | 
            -
                                            <strong>Session ID:</strong> ${toolCall.session_id}
         | 
| 1877 | 
            -
                                        </div>
         | 
| 1878 | 
            -
                                    ` : ''}
         | 
| 1879 | 
            -
                                </div>
         | 
| 1880 | 
            -
                                
         | 
| 1881 | 
            -
                                ${formattedResultSummary && formattedResultSummary !== 'No summary available' ? `
         | 
| 1882 | 
            -
                                    <div class="result-section">
         | 
| 1883 | 
            -
                                        <div class="structured-view-header">
         | 
| 1884 | 
            -
                                            <h4>๐ Result Summary</h4>
         | 
| 1885 | 
            -
                                        </div>
         | 
| 1886 | 
            -
                                        <div class="structured-data">
         | 
| 1887 | 
            -
                                            <div class="result-summary" style="white-space: pre-wrap; max-height: 200px; overflow-y: auto; padding: 10px; background: #f8fafc; border-radius: 6px; font-family: monospace; font-size: 12px; line-height: 1.4;">
         | 
| 1888 | 
            -
                                                ${formattedResultSummary}
         | 
| 1889 | 
            -
                                            </div>
         | 
| 1890 | 
            -
                                            ${typeof resultSummary === 'object' && resultSummary !== null ? `
         | 
| 1891 | 
            -
                                                <div class="result-summary-json" style="white-space: pre-wrap; max-height: 200px; overflow-y: auto; padding: 10px; background: #f0f9ff; border-radius: 6px; font-family: monospace; font-size: 11px; line-height: 1.3; margin-top: 10px;">
         | 
| 1892 | 
            -
                                                    <h5 style="margin: 0 0 8px 0; font-size: 11px; color: #4a5568;">Raw JSON:</h5>
         | 
| 1893 | 
            -
                                                    ${JSON.stringify(resultSummary, null, 2)}
         | 
| 1894 | 
            -
                                                </div>
         | 
| 1895 | 
            -
                                            ` : ''}
         | 
| 1896 | 
            -
                                        </div>
         | 
| 1897 | 
            -
                                    </div>
         | 
| 1898 | 
            -
                                ` : ''}
         | 
| 1899 | 
            -
                                
         | 
| 1900 | 
            -
                                ${toolName === 'TodoWrite' && parameters.todos ? `
         | 
| 1901 | 
            -
                                    <div class="todos-section">
         | 
| 1902 | 
            -
                                        <div class="todos-list">
         | 
| 1903 | 
            -
                                            ${parameters.todos.map(todo => {
         | 
| 1904 | 
            -
                                                const statusIcon = {
         | 
| 1905 | 
            -
                                                    'pending': 'โณ',
         | 
| 1906 | 
            -
                                                    'in_progress': '๐',
         | 
| 1907 | 
            -
                                                    'completed': 'โ
'
         | 
| 1908 | 
            -
                                                }[todo.status] || 'โ';
         | 
| 1909 | 
            -
                                                
         | 
| 1910 | 
            -
                                                const priorityColor = {
         | 
| 1911 | 
            -
                                                    'high': '#dc2626',
         | 
| 1912 | 
            -
                                                    'medium': '#f59e0b',
         | 
| 1913 | 
            -
                                                    'low': '#10b981'
         | 
| 1914 | 
            -
                                                }[todo.priority] || '#6b7280';
         | 
| 1915 | 
            -
                                                
         | 
| 1916 | 
            -
                                                return `
         | 
| 1917 | 
            -
                                                    <div class="todo-item" style="padding: 8px; margin: 4px 0; border-left: 3px solid ${priorityColor}; background: #f8fafc; border-radius: 4px;">
         | 
| 1918 | 
            -
                                                        <div style="display: flex; align-items: center; gap: 8px;">
         | 
| 1919 | 
            -
                                                            <span style="font-size: 16px;">${statusIcon}</span>
         | 
| 1920 | 
            -
                                                            <span style="font-weight: 500; color: #374151;">${todo.content}</span>
         | 
| 1921 | 
            -
                                                            <span style="font-size: 11px; color: ${priorityColor}; text-transform: uppercase; font-weight: 600; margin-left: auto;">${todo.priority}</span>
         | 
| 1922 | 
            -
                                                        </div>
         | 
| 1923 | 
            -
                                                    </div>
         | 
| 1924 | 
            -
                                                `;
         | 
| 1925 | 
            -
                                            }).join('')}
         | 
| 1926 | 
            -
                                        </div>
         | 
| 1927 | 
            -
                                    </div>
         | 
| 1928 | 
            -
                                ` : ''}
         | 
| 1929 | 
            -
                                
         | 
| 1930 | 
            -
                                ${Object.keys(parameters).length > 0 && toolName !== 'TodoWrite' ? `
         | 
| 1931 | 
            -
                                    <div class="parameters-section">
         | 
| 1932 | 
            -
                                        <div class="structured-view-header">
         | 
| 1933 | 
            -
                                            <h4>โ๏ธ Parameters</h4>
         | 
| 1934 | 
            -
                                        </div>
         | 
| 1935 | 
            -
                                        <div class="structured-data">
         | 
| 1936 | 
            -
                                            <pre style="white-space: pre-wrap; font-family: monospace; font-size: 12px; line-height: 1.4;">${JSON.stringify(parameters, null, 2)}</pre>
         | 
| 1937 | 
            -
                                        </div>
         | 
| 1938 | 
            -
                                    </div>
         | 
| 1939 | 
            -
                                ` : ''}
         | 
| 1940 | 
            -
                                
         | 
| 1941 | 
            -
                                <div class="raw-data-section">
         | 
| 1942 | 
            -
                                    <div class="structured-view-header">
         | 
| 1943 | 
            -
                                        <h4>๐ง JSON Event Data</h4>
         | 
| 1944 | 
            -
                                    </div>
         | 
| 1945 | 
            -
                                    <div class="structured-data">
         | 
| 1946 | 
            -
                                        <div style="margin-bottom: 15px;">
         | 
| 1947 | 
            -
                                            <strong>Pre-execution Event:</strong>
         | 
| 1948 | 
            -
                                            <pre style="white-space: pre-wrap; font-family: monospace; font-size: 11px; line-height: 1.3; background: #f0f9ff; padding: 8px; border-radius: 4px; max-height: 300px; overflow-y: auto;">${preEvent ? JSON.stringify(preEvent, null, 2) : 'No pre-event data'}</pre>
         | 
| 1949 | 
            -
                                        </div>
         | 
| 1950 | 
            -
                                        <div>
         | 
| 1951 | 
            -
                                            <strong>Post-execution Event:</strong>
         | 
| 1952 | 
            -
                                            <pre style="white-space: pre-wrap; font-family: monospace; font-size: 11px; line-height: 1.3; background: #f0f9ff; padding: 8px; border-radius: 4px; max-height: 300px; overflow-y: auto;">${postEvent ? JSON.stringify(postEvent, null, 2) : 'No post-event data'}</pre>
         | 
| 1953 | 
            -
                                        </div>
         | 
| 1954 | 
            -
                                    </div>
         | 
| 1955 | 
            -
                                </div>
         | 
| 1956 | 
            -
                            </div>
         | 
| 1957 | 
            -
                        </div>
         | 
| 1958 | 
            -
                    `;
         | 
| 1959 | 
            -
             | 
| 1960 | 
            -
                    // Special handling for TodoWrite - show only checklist with standard header
         | 
| 1961 | 
            -
                    if (toolName === 'TodoWrite' && parameters.todos) {
         | 
| 1962 | 
            -
                        // Create contextual header matching module-viewer pattern
         | 
| 1963 | 
            -
                        const contextualHeader = `
         | 
| 1964 | 
            -
                            <div class="contextual-header">
         | 
| 1965 | 
            -
                                <h3 class="contextual-header-text">TodoWrite: ${agentName} ${this.formatTimestamp(toolCall.timestamp)}</h3>
         | 
| 1966 | 
            -
                            </div>
         | 
| 1967 | 
            -
                        `;
         | 
| 1968 | 
            -
                        
         | 
| 1969 | 
            -
                        const todoContent = `
         | 
| 1970 | 
            -
                            <div class="todo-checklist">
         | 
| 1971 | 
            -
                                ${parameters.todos.map(todo => {
         | 
| 1972 | 
            -
                                    const statusIcon = {
         | 
| 1973 | 
            -
                                        'pending': 'โณ',
         | 
| 1974 | 
            -
                                        'in_progress': '๐',
         | 
| 1975 | 
            -
                                        'completed': 'โ
'
         | 
| 1976 | 
            -
                                    }[todo.status] || 'โ';
         | 
| 1977 | 
            -
                                    
         | 
| 1978 | 
            -
                                    const priorityIcon = {
         | 
| 1979 | 
            -
                                        'high': '๐ด',
         | 
| 1980 | 
            -
                                        'medium': '๐ก',
         | 
| 1981 | 
            -
                                        'low': '๐ข'
         | 
| 1982 | 
            -
                                    }[todo.priority] || '๐ก';
         | 
| 1983 | 
            -
                                    
         | 
| 1984 | 
            -
                                    return `
         | 
| 1985 | 
            -
                                        <div class="todo-item todo-${todo.status || 'pending'}">
         | 
| 1986 | 
            -
                                            <span class="todo-status">${statusIcon}</span>
         | 
| 1987 | 
            -
                                            <span class="todo-content">${todo.content || 'No content'}</span>
         | 
| 1988 | 
            -
                                            <span class="todo-priority priority-${todo.priority || 'medium'}">${priorityIcon}</span>
         | 
| 1989 | 
            -
                                        </div>
         | 
| 1990 | 
            -
                                    `;
         | 
| 1991 | 
            -
                                }).join('')}
         | 
| 1992 | 
            -
                            </div>
         | 
| 1993 | 
            -
                        `;
         | 
| 1994 | 
            -
                        
         | 
| 1995 | 
            -
                        if (this.moduleViewer.dataContainer) {
         | 
| 1996 | 
            -
                            this.moduleViewer.dataContainer.innerHTML = contextualHeader + todoContent;
         | 
| 1997 | 
            -
                        }
         | 
| 1998 | 
            -
                        if (this.moduleViewer.jsonContainer) {
         | 
| 1999 | 
            -
                            const toolCallData = {
         | 
| 2000 | 
            -
                                toolCall: toolCall,
         | 
| 2001 | 
            -
                                preEvent: preEvent,
         | 
| 2002 | 
            -
                                postEvent: postEvent
         | 
| 2003 | 
            -
                            };
         | 
| 2004 | 
            -
                            this.moduleViewer.jsonContainer.innerHTML = `<pre>${JSON.stringify(toolCallData, null, 2)}</pre>`;
         | 
| 2005 | 
            -
                        }
         | 
| 2006 | 
            -
                    } else {
         | 
| 2007 | 
            -
                        // For other tools, use the module viewer's ingest method with pre-event
         | 
| 2008 | 
            -
                        if (this.moduleViewer && preEvent) {
         | 
| 2009 | 
            -
                            this.moduleViewer.ingest(preEvent);
         | 
| 2010 | 
            -
                        }
         | 
| 2011 | 
            -
                    }
         | 
| 2012 | 
            -
                }
         | 
| 2013 | 
            -
             | 
| 2014 | 
            -
             | 
| 2015 | 
            -
             | 
| 2016 | 
            -
                /**
         | 
| 2017 | 
            -
                 * Show detailed file operations in module viewer
         | 
| 2018 | 
            -
                 */
         | 
| 2019 | 
            -
                showFileDetails(filePath) {
         | 
| 2020 | 
            -
                    const fileData = this.fileOperations.get(filePath);
         | 
| 2021 | 
            -
                    if (!fileData) return;
         | 
| 2022 | 
            -
             | 
| 2023 | 
            -
                    // Filter operations by selected session if applicable
         | 
| 2024 | 
            -
                    let operations = fileData.operations;
         | 
| 2025 | 
            -
                    if (this.selectedSessionId) {
         | 
| 2026 | 
            -
                        operations = operations.filter(op => op.sessionId === this.selectedSessionId);
         | 
| 2027 | 
            -
                        if (operations.length === 0) {
         | 
| 2028 | 
            -
                            // No operations from this session
         | 
| 2029 | 
            -
                            this.moduleViewer.showErrorMessage(
         | 
| 2030 | 
            -
                                'No Operations in Selected Session',
         | 
| 2031 | 
            -
                                `This file has no operations from the selected session.`
         | 
| 2032 | 
            -
                            );
         | 
| 2033 | 
            -
                            return;
         | 
| 2034 | 
            -
                        }
         | 
| 2035 | 
            -
                    }
         | 
| 2036 | 
            -
             | 
| 2037 | 
            -
                    // Get file name from path for header
         | 
| 2038 | 
            -
                    const fileName = filePath.split('/').pop() || filePath;
         | 
| 2039 | 
            -
                    const lastOp = operations[operations.length - 1];
         | 
| 2040 | 
            -
                    const headerTimestamp = this.formatTimestamp(lastOp.timestamp);
         | 
| 2041 | 
            -
                    
         | 
| 2042 | 
            -
                    // Create contextual header matching module-viewer pattern
         | 
| 2043 | 
            -
                    const contextualHeader = `
         | 
| 2044 | 
            -
                        <div class="contextual-header">
         | 
| 2045 | 
            -
                            <h3 class="contextual-header-text">File: ${fileName} ${headerTimestamp}</h3>
         | 
| 2046 | 
            -
                        </div>
         | 
| 2047 | 
            -
                    `;
         | 
| 2048 | 
            -
             | 
| 2049 | 
            -
                    const content = `
         | 
| 2050 | 
            -
                        <div class="structured-view-section">
         | 
| 2051 | 
            -
                            <div class="file-details">
         | 
| 2052 | 
            -
                                <div class="file-path-display">
         | 
| 2053 | 
            -
                                    <strong>Full Path:</strong> ${filePath}
         | 
| 2054 | 
            -
                                </div>
         | 
| 2055 | 
            -
                                <div class="operations-list">
         | 
| 2056 | 
            -
                                    ${operations.map(op => `
         | 
| 2057 | 
            -
                                        <div class="operation-item">
         | 
| 2058 | 
            -
                                            <div class="operation-header">
         | 
| 2059 | 
            -
                                                <span class="operation-icon">${this.getOperationIcon(op.operation)}</span>
         | 
| 2060 | 
            -
                                                <span class="operation-type">${op.operation}</span>
         | 
| 2061 | 
            -
                                                <span class="operation-timestamp">${new Date(op.timestamp).toLocaleString()}</span>
         | 
| 2062 | 
            -
                                                ${(['edit', 'write'].includes(op.operation)) ? `
         | 
| 2063 | 
            -
                                                    <span class="git-diff-icon" 
         | 
| 2064 | 
            -
                                                          onclick="showGitDiffModal('${filePath}', '${op.timestamp}')"
         | 
| 2065 | 
            -
                                                          title="View git diff for this file operation"
         | 
| 2066 | 
            -
                                                          style="margin-left: 8px; cursor: pointer; font-size: 16px;">
         | 
| 2067 | 
            -
                                                        ๐
         | 
| 2068 | 
            -
                                                    </span>
         | 
| 2069 | 
            -
                                                ` : ''}
         | 
| 2070 | 
            -
                                            </div>
         | 
| 2071 | 
            -
                                            <div class="operation-details">
         | 
| 2072 | 
            -
                                                <strong>Agent:</strong> ${op.agent}<br>
         | 
| 2073 | 
            -
                                                <strong>Session:</strong> ${op.sessionId ? op.sessionId.substring(0, 8) + '...' : 'Unknown'}
         | 
| 2074 | 
            -
                                                ${op.details ? `<br><strong>Details:</strong> ${op.details}` : ''}
         | 
| 2075 | 
            -
                                            </div>
         | 
| 2076 | 
            -
                                        </div>
         | 
| 2077 | 
            -
                                    `).join('')}
         | 
| 2078 | 
            -
                                </div>
         | 
| 2079 | 
            -
                            </div>
         | 
| 2080 | 
            -
                        </div>
         | 
| 2081 | 
            -
                    `;
         | 
| 2082 | 
            -
             | 
| 2083 | 
            -
                    // Use the new dual-pane approach for file details with standard header
         | 
| 2084 | 
            -
                    if (this.moduleViewer.dataContainer) {
         | 
| 2085 | 
            -
                        this.moduleViewer.dataContainer.innerHTML = contextualHeader + content;
         | 
| 2086 | 
            -
                    }
         | 
| 2087 | 
            -
                    if (this.moduleViewer.jsonContainer) {
         | 
| 2088 | 
            -
                        // Show the file data with operations
         | 
| 2089 | 
            -
                        this.moduleViewer.jsonContainer.innerHTML = `<pre>${JSON.stringify(fileData, null, 2)}</pre>`;
         | 
| 2090 | 
            -
                    }
         | 
| 2091 | 
            -
                }
         | 
| 2092 | 
            -
             | 
| 2093 | 
            -
                /**
         | 
| 2094 | 
            -
                 * Update file operations from events
         | 
| 2095 | 
            -
                 */
         | 
| 2096 | 
            -
                updateFileOperations(events) {
         | 
| 2097 | 
            -
                    // Clear existing data
         | 
| 2098 | 
            -
                    this.fileOperations.clear();
         | 
| 2099 | 
            -
             | 
| 2100 | 
            -
                    console.log('updateFileOperations - processing', events.length, 'events');
         | 
| 2101 | 
            -
             | 
| 2102 | 
            -
                    // Group events by session and timestamp to match pre/post pairs
         | 
| 2103 | 
            -
                    const eventPairs = new Map(); // Key: session_id + timestamp + tool_name
         | 
| 2104 | 
            -
                    let fileOperationCount = 0;
         | 
| 2105 | 
            -
                    
         | 
| 2106 | 
            -
                    // First pass: collect all tool events and group them
         | 
| 2107 | 
            -
                    events.forEach((event, index) => {
         | 
| 2108 | 
            -
                        const isFileOp = this.isFileOperation(event);
         | 
| 2109 | 
            -
                        if (isFileOp) fileOperationCount++;
         | 
| 2110 | 
            -
                        
         | 
| 2111 | 
            -
                        if (index < 5) { // Debug first 5 events with more detail
         | 
| 2112 | 
            -
                            console.log(`Event ${index}:`, {
         | 
| 2113 | 
            -
                                type: event.type,
         | 
| 2114 | 
            -
                                subtype: event.subtype,
         | 
| 2115 | 
            -
                                tool_name: event.tool_name,
         | 
| 2116 | 
            -
                                tool_parameters: event.tool_parameters,
         | 
| 2117 | 
            -
                                isFileOp: isFileOp
         | 
| 2118 | 
            -
                            });
         | 
| 2119 | 
            -
                        }
         | 
| 2120 | 
            -
                        
         | 
| 2121 | 
            -
                        if (isFileOp) {
         | 
| 2122 | 
            -
                            const toolName = event.tool_name;
         | 
| 2123 | 
            -
                            const sessionId = event.session_id || 'unknown';
         | 
| 2124 | 
            -
                            const eventKey = `${sessionId}_${toolName}_${Math.floor(new Date(event.timestamp).getTime() / 1000)}`; // Group by second
         | 
| 2125 | 
            -
                            
         | 
| 2126 | 
            -
                            if (!eventPairs.has(eventKey)) {
         | 
| 2127 | 
            -
                                eventPairs.set(eventKey, {
         | 
| 2128 | 
            -
                                    pre_event: null,
         | 
| 2129 | 
            -
                                    post_event: null,
         | 
| 2130 | 
            -
                                    tool_name: toolName,
         | 
| 2131 | 
            -
                                    session_id: sessionId
         | 
| 2132 | 
            -
                                });
         | 
| 2133 | 
            -
                            }
         | 
| 2134 | 
            -
                            
         | 
| 2135 | 
            -
                            const pair = eventPairs.get(eventKey);
         | 
| 2136 | 
            -
                            if (event.subtype === 'pre_tool' || event.type === 'hook' && !event.subtype.includes('post')) {
         | 
| 2137 | 
            -
                                pair.pre_event = event;
         | 
| 2138 | 
            -
                            } else if (event.subtype === 'post_tool' || event.subtype.includes('post')) {
         | 
| 2139 | 
            -
                                pair.post_event = event;
         | 
| 2140 | 
            -
                            } else {
         | 
| 2141 | 
            -
                                // For events without clear pre/post distinction, treat as both
         | 
| 2142 | 
            -
                                pair.pre_event = event;
         | 
| 2143 | 
            -
                                pair.post_event = event;
         | 
| 2144 | 
            -
                            }
         | 
| 2145 | 
            -
                        }
         | 
| 2146 | 
            -
                    });
         | 
| 2147 | 
            -
                    
         | 
| 2148 | 
            -
                    console.log('updateFileOperations - found', fileOperationCount, 'file operations in', eventPairs.size, 'event pairs');
         | 
| 2149 | 
            -
                    
         | 
| 2150 | 
            -
                    // Second pass: extract file paths and operations from paired events
         | 
| 2151 | 
            -
                    eventPairs.forEach((pair, key) => {
         | 
| 2152 | 
            -
                        const filePath = this.extractFilePathFromPair(pair);
         | 
| 2153 | 
            -
                        
         | 
| 2154 | 
            -
                        if (filePath) {
         | 
| 2155 | 
            -
                            console.log('File operation detected for:', filePath, 'from pair:', key);
         | 
| 2156 | 
            -
                            
         | 
| 2157 | 
            -
                            if (!this.fileOperations.has(filePath)) {
         | 
| 2158 | 
            -
                                this.fileOperations.set(filePath, {
         | 
| 2159 | 
            -
                                    path: filePath,
         | 
| 2160 | 
            -
                                    operations: [],
         | 
| 2161 | 
            -
                                    lastOperation: null
         | 
| 2162 | 
            -
                                });
         | 
| 2163 | 
            -
                            }
         | 
| 2164 | 
            -
             | 
| 2165 | 
            -
                            const fileData = this.fileOperations.get(filePath);
         | 
| 2166 | 
            -
                            const operation = this.getFileOperationFromPair(pair);
         | 
| 2167 | 
            -
                            const timestamp = pair.post_event?.timestamp || pair.pre_event?.timestamp;
         | 
| 2168 | 
            -
                            
         | 
| 2169 | 
            -
                            const agentInfo = this.extractAgentFromPair(pair);
         | 
| 2170 | 
            -
                            const workingDirectory = this.extractWorkingDirectoryFromPair(pair);
         | 
| 2171 | 
            -
                            
         | 
| 2172 | 
            -
                            fileData.operations.push({
         | 
| 2173 | 
            -
                                operation: operation,
         | 
| 2174 | 
            -
                                timestamp: timestamp,
         | 
| 2175 | 
            -
                                agent: agentInfo.name,
         | 
| 2176 | 
            -
                                confidence: agentInfo.confidence,
         | 
| 2177 | 
            -
                                sessionId: pair.session_id,
         | 
| 2178 | 
            -
                                details: this.getFileOperationDetailsFromPair(pair),
         | 
| 2179 | 
            -
                                workingDirectory: workingDirectory
         | 
| 2180 | 
            -
                            });
         | 
| 2181 | 
            -
                            fileData.lastOperation = timestamp;
         | 
| 2182 | 
            -
                        } else {
         | 
| 2183 | 
            -
                            console.log('No file path found for pair:', key, pair);
         | 
| 2184 | 
            -
                        }
         | 
| 2185 | 
            -
                    });
         | 
| 2186 | 
            -
                    
         | 
| 2187 | 
            -
                    console.log('updateFileOperations - final result:', this.fileOperations.size, 'file operations');
         | 
| 2188 | 
            -
                    if (this.fileOperations.size > 0) {
         | 
| 2189 | 
            -
                        console.log('File operations map:', Array.from(this.fileOperations.entries()));
         | 
| 2190 | 
            -
                    }
         | 
| 2191 | 
            -
                }
         | 
| 2192 | 
            -
             | 
| 2193 | 
            -
                /**
         | 
| 2194 | 
            -
                 * Update tool calls from events - pairs pre/post tool events into complete tool calls
         | 
| 2195 | 
            -
                 */
         | 
| 2196 | 
            -
                updateToolCalls(events) {
         | 
| 2197 | 
            -
                    // Clear existing data
         | 
| 2198 | 
            -
                    this.toolCalls.clear();
         | 
| 2199 | 
            -
             | 
| 2200 | 
            -
                    console.log('updateToolCalls - processing', events.length, 'events');
         | 
| 2201 | 
            -
             | 
| 2202 | 
            -
                    // Group events by session and timestamp to match pre/post pairs
         | 
| 2203 | 
            -
                    const toolCallPairs = new Map(); // Key: session_id + timestamp + tool_name
         | 
| 2204 | 
            -
                    let toolOperationCount = 0;
         | 
| 2205 | 
            -
                    
         | 
| 2206 | 
            -
                    // First pass: collect all tool events and group them
         | 
| 2207 | 
            -
                    events.forEach((event, index) => {
         | 
| 2208 | 
            -
                        const isToolOp = this.isToolOperation(event);
         | 
| 2209 | 
            -
                        if (isToolOp) toolOperationCount++;
         | 
| 2210 | 
            -
                        
         | 
| 2211 | 
            -
                        if (index < 5) { // Debug first 5 events with more detail
         | 
| 2212 | 
            -
                            console.log(`Tool Event ${index}:`, {
         | 
| 2213 | 
            -
                                type: event.type,
         | 
| 2214 | 
            -
                                subtype: event.subtype,
         | 
| 2215 | 
            -
                                tool_name: event.tool_name,
         | 
| 2216 | 
            -
                                tool_parameters: event.tool_parameters,
         | 
| 2217 | 
            -
                                isToolOp: isToolOp
         | 
| 2218 | 
            -
                            });
         | 
| 2219 | 
            -
                        }
         | 
| 2220 | 
            -
                        
         | 
| 2221 | 
            -
                        if (isToolOp) {
         | 
| 2222 | 
            -
                            const toolName = event.tool_name;
         | 
| 2223 | 
            -
                            const sessionId = event.session_id || 'unknown';
         | 
| 2224 | 
            -
                            const eventKey = `${sessionId}_${toolName}_${Math.floor(new Date(event.timestamp).getTime() / 1000)}`; // Group by second
         | 
| 2225 | 
            -
                            
         | 
| 2226 | 
            -
                            if (!toolCallPairs.has(eventKey)) {
         | 
| 2227 | 
            -
                                toolCallPairs.set(eventKey, {
         | 
| 2228 | 
            -
                                    pre_event: null,
         | 
| 2229 | 
            -
                                    post_event: null,
         | 
| 2230 | 
            -
                                    tool_name: toolName,
         | 
| 2231 | 
            -
                                    session_id: sessionId,
         | 
| 2232 | 
            -
                                    operation_type: null,
         | 
| 2233 | 
            -
                                    timestamp: null,
         | 
| 2234 | 
            -
                                    duration_ms: null,
         | 
| 2235 | 
            -
                                    success: null,
         | 
| 2236 | 
            -
                                    exit_code: null,
         | 
| 2237 | 
            -
                                    result_summary: null,
         | 
| 2238 | 
            -
                                    agent_type: null
         | 
| 2239 | 
            -
                                });
         | 
| 2240 | 
            -
                            }
         | 
| 2241 | 
            -
                            
         | 
| 2242 | 
            -
                            const pair = toolCallPairs.get(eventKey);
         | 
| 2243 | 
            -
                            if (event.subtype === 'pre_tool' || (event.type === 'hook' && !event.subtype.includes('post'))) {
         | 
| 2244 | 
            -
                                pair.pre_event = event;
         | 
| 2245 | 
            -
                                pair.timestamp = event.timestamp;
         | 
| 2246 | 
            -
                                pair.operation_type = event.operation_type || 'tool_execution';
         | 
| 2247 | 
            -
                                pair.agent_type = event.agent_type || event.subagent_type || 'PM';
         | 
| 2248 | 
            -
                            } else if (event.subtype === 'post_tool' || event.subtype.includes('post')) {
         | 
| 2249 | 
            -
                                pair.post_event = event;
         | 
| 2250 | 
            -
                                pair.duration_ms = event.duration_ms;
         | 
| 2251 | 
            -
                                pair.success = event.success;
         | 
| 2252 | 
            -
                                pair.exit_code = event.exit_code;
         | 
| 2253 | 
            -
                                pair.result_summary = event.result_summary;
         | 
| 2254 | 
            -
                                if (!pair.agent_type) {
         | 
| 2255 | 
            -
                                    pair.agent_type = event.agent_type || event.subagent_type || 'PM';
         | 
| 2256 | 
            -
                                }
         | 
| 2257 | 
            -
                            } else {
         | 
| 2258 | 
            -
                                // For events without clear pre/post distinction, treat as both
         | 
| 2259 | 
            -
                                pair.pre_event = event;
         | 
| 2260 | 
            -
                                pair.post_event = event;
         | 
| 2261 | 
            -
                                pair.timestamp = event.timestamp;
         | 
| 2262 | 
            -
                                pair.agent_type = event.agent_type || event.subagent_type || 'PM';
         | 
| 2263 | 
            -
                            }
         | 
| 2264 | 
            -
                        }
         | 
| 2265 | 
            -
                    });
         | 
| 2266 | 
            -
                    
         | 
| 2267 | 
            -
                    console.log('updateToolCalls - found', toolOperationCount, 'tool operations in', toolCallPairs.size, 'tool call pairs');
         | 
| 2268 | 
            -
                    
         | 
| 2269 | 
            -
                    // Second pass: store complete tool calls
         | 
| 2270 | 
            -
                    toolCallPairs.forEach((pair, key) => {
         | 
| 2271 | 
            -
                        // Ensure we have at least a pre_event or post_event
         | 
| 2272 | 
            -
                        if (pair.pre_event || pair.post_event) {
         | 
| 2273 | 
            -
                            console.log('Tool call detected for:', pair.tool_name, 'from pair:', key);
         | 
| 2274 | 
            -
                            this.toolCalls.set(key, pair);
         | 
| 2275 | 
            -
                        } else {
         | 
| 2276 | 
            -
                            console.log('No valid tool call found for pair:', key, pair);
         | 
| 2277 | 
            -
                        }
         | 
| 2278 | 
            -
                    });
         | 
| 2279 | 
            -
                    
         | 
| 2280 | 
            -
                    console.log('updateToolCalls - final result:', this.toolCalls.size, 'tool calls');
         | 
| 2281 | 
            -
                    if (this.toolCalls.size > 0) {
         | 
| 2282 | 
            -
                        console.log('Tool calls map:', Array.from(this.toolCalls.entries()));
         | 
| 2283 | 
            -
                    }
         | 
| 2284 | 
            -
                }
         | 
| 2285 | 
            -
             | 
| 2286 | 
            -
                /**
         | 
| 2287 | 
            -
                 * Check if event is a tool operation
         | 
| 2288 | 
            -
                 */
         | 
| 2289 | 
            -
                isToolOperation(event) {
         | 
| 2290 | 
            -
                    const type = event.type || '';
         | 
| 2291 | 
            -
                    const subtype = event.subtype || '';
         | 
| 2292 | 
            -
                    
         | 
| 2293 | 
            -
                    // Check for hook events with tool subtypes
         | 
| 2294 | 
            -
                    const isHookToolEvent = type === 'hook' && (
         | 
| 2295 | 
            -
                        subtype.includes('tool') || 
         | 
| 2296 | 
            -
                        subtype.includes('pre_') || 
         | 
| 2297 | 
            -
                        subtype.includes('post_')
         | 
| 2298 | 
            -
                    );
         | 
| 2299 | 
            -
                    
         | 
| 2300 | 
            -
                    // Events with tool_name
         | 
| 2301 | 
            -
                    const hasToolName = event.tool_name;
         | 
| 2302 | 
            -
                    
         | 
| 2303 | 
            -
                    // Events with tools array (multiple tools)
         | 
| 2304 | 
            -
                    const hasToolsArray = event.tools && Array.isArray(event.tools);
         | 
| 2305 | 
            -
                    
         | 
| 2306 | 
            -
                    // Legacy hook events with tool patterns (backward compatibility)
         | 
| 2307 | 
            -
                    const isLegacyHookEvent = type.startsWith('hook.') && (
         | 
| 2308 | 
            -
                        type.includes('tool') || 
         | 
| 2309 | 
            -
                        type.includes('pre') || 
         | 
| 2310 | 
            -
                        type.includes('post')
         | 
| 2311 | 
            -
                    );
         | 
| 2312 | 
            -
                    
         | 
| 2313 | 
            -
                    return isHookToolEvent || hasToolName || hasToolsArray || isLegacyHookEvent;
         | 
| 2314 | 
            -
                }
         | 
| 2315 | 
            -
             | 
| 2316 | 
            -
                /**
         | 
| 2317 | 
            -
                 * Check if event is a file operation
         | 
| 2318 | 
            -
                 */
         | 
| 2319 | 
            -
                isFileOperation(event) {
         | 
| 2320 | 
            -
                    const toolName = event.tool_name;
         | 
| 2321 | 
            -
                    const fileTools = ['Read', 'Write', 'Edit', 'MultiEdit', 'Glob', 'LS', 'NotebookRead', 'NotebookEdit', 'Grep'];
         | 
| 2322 | 
            -
                    
         | 
| 2323 | 
            -
                    // Check for direct tool name match
         | 
| 2324 | 
            -
                    if (fileTools.includes(toolName)) {
         | 
| 2325 | 
            -
                        console.log('isFileOperation - direct tool match:', toolName);
         | 
| 2326 | 
            -
                        return true;
         | 
| 2327 | 
            -
                    }
         | 
| 2328 | 
            -
                    
         | 
| 2329 | 
            -
                    // Check for hook events that involve file tools (updated for new structure)
         | 
| 2330 | 
            -
                    const type = event.type || '';
         | 
| 2331 | 
            -
                    const subtype = event.subtype || '';
         | 
| 2332 | 
            -
                    
         | 
| 2333 | 
            -
                    // Check both legacy format and new format
         | 
| 2334 | 
            -
                    const isHookEvent = type === 'hook' || type.startsWith('hook.');
         | 
| 2335 | 
            -
                    
         | 
| 2336 | 
            -
                    if (isHookEvent) {
         | 
| 2337 | 
            -
                        // Check if tool_name indicates file operation
         | 
| 2338 | 
            -
                        if (fileTools.includes(event.tool_name)) {
         | 
| 2339 | 
            -
                            console.log('isFileOperation - hook tool match:', event.tool_name);
         | 
| 2340 | 
            -
                            return true;
         | 
| 2341 | 
            -
                        }
         | 
| 2342 | 
            -
                        
         | 
| 2343 | 
            -
                        // Check if parameters suggest file operation
         | 
| 2344 | 
            -
                        const params = event.tool_parameters || {};
         | 
| 2345 | 
            -
                        const hasFileParams = !!(params.file_path || params.path || params.notebook_path || params.pattern);
         | 
| 2346 | 
            -
                        
         | 
| 2347 | 
            -
                        // Also check top-level event for file parameters (flat structure)
         | 
| 2348 | 
            -
                        const hasDirectFileParams = !!(event.file_path || event.path || event.notebook_path || event.pattern);
         | 
| 2349 | 
            -
                        
         | 
| 2350 | 
            -
                        const hasAnyFileParams = hasFileParams || hasDirectFileParams;
         | 
| 2351 | 
            -
                        if (hasAnyFileParams) {
         | 
| 2352 | 
            -
                            console.log('isFileOperation - file params match:', { hasFileParams, hasDirectFileParams, params, directParams: { file_path: event.file_path, path: event.path } });
         | 
| 2353 | 
            -
                        }
         | 
| 2354 | 
            -
                        
         | 
| 2355 | 
            -
                        return hasAnyFileParams;
         | 
| 2356 | 
            -
                    }
         | 
| 2357 | 
            -
                    
         | 
| 2358 | 
            -
                    return false;
         | 
| 2359 | 
            -
                }
         | 
| 2360 | 
            -
             | 
| 2361 | 
            -
                /**
         | 
| 2362 | 
            -
                 * Extract file path from event
         | 
| 2363 | 
            -
                 */
         | 
| 2364 | 
            -
                extractFilePath(event) {
         | 
| 2365 | 
            -
                    // Check tool_parameters first
         | 
| 2366 | 
            -
                    const params = event.tool_parameters;
         | 
| 2367 | 
            -
                    if (params) {
         | 
| 2368 | 
            -
                        if (params.file_path) return params.file_path;
         | 
| 2369 | 
            -
                        if (params.path) return params.path;
         | 
| 2370 | 
            -
                        if (params.notebook_path) return params.notebook_path;
         | 
| 2371 | 
            -
                    }
         | 
| 2372 | 
            -
                    
         | 
| 2373 | 
            -
                    // Check top-level event (flat structure)
         | 
| 2374 | 
            -
                    if (event.file_path) return event.file_path;
         | 
| 2375 | 
            -
                    if (event.path) return event.path;
         | 
| 2376 | 
            -
                    if (event.notebook_path) return event.notebook_path;
         | 
| 2377 | 
            -
                    
         | 
| 2378 | 
            -
                    // Check tool_input if available (sometimes path is here)
         | 
| 2379 | 
            -
                    if (event.tool_input) {
         | 
| 2380 | 
            -
                        if (event.tool_input.file_path) return event.tool_input.file_path;
         | 
| 2381 | 
            -
                        if (event.tool_input.path) return event.tool_input.path;
         | 
| 2382 | 
            -
                        if (event.tool_input.notebook_path) return event.tool_input.notebook_path;
         | 
| 2383 | 
            -
                    }
         | 
| 2384 | 
            -
                    
         | 
| 2385 | 
            -
                    // Check result/output if available (sometimes path is in result)
         | 
| 2386 | 
            -
                    if (event.result) {
         | 
| 2387 | 
            -
                        if (event.result.file_path) return event.result.file_path;
         | 
| 2388 | 
            -
                        if (event.result.path) return event.result.path;
         | 
| 2389 | 
            -
                    }
         | 
| 2390 | 
            -
                    
         | 
| 2391 | 
            -
                    return null;
         | 
| 2392 | 
            -
                }
         | 
| 2393 | 
            -
                
         | 
| 2394 | 
            -
                /**
         | 
| 2395 | 
            -
                 * Extract file path from paired pre/post events
         | 
| 2396 | 
            -
                 */
         | 
| 2397 | 
            -
                extractFilePathFromPair(pair) {
         | 
| 2398 | 
            -
                    // Try pre_event first, then post_event
         | 
| 2399 | 
            -
                    const preEvent = pair.pre_event;
         | 
| 2400 | 
            -
                    const postEvent = pair.post_event;
         | 
| 2401 | 
            -
                    
         | 
| 2402 | 
            -
                    if (preEvent) {
         | 
| 2403 | 
            -
                        const prePath = this.extractFilePath(preEvent);
         | 
| 2404 | 
            -
                        if (prePath) return prePath;
         | 
| 2405 | 
            -
                    }
         | 
| 2406 | 
            -
                    
         | 
| 2407 | 
            -
                    if (postEvent) {
         | 
| 2408 | 
            -
                        const postPath = this.extractFilePath(postEvent);
         | 
| 2409 | 
            -
                        if (postPath) return postPath;
         | 
| 2410 | 
            -
                    }
         | 
| 2411 | 
            -
                    
         | 
| 2412 | 
            -
                    return null;
         | 
| 2413 | 
            -
                }
         | 
| 2414 | 
            -
             | 
| 2415 | 
            -
                /**
         | 
| 2416 | 
            -
                 * Get file operation type
         | 
| 2417 | 
            -
                 */
         | 
| 2418 | 
            -
                getFileOperation(event) {
         | 
| 2419 | 
            -
                    const toolName = event.tool_name;
         | 
| 2420 | 
            -
                    const operationMap = {
         | 
| 2421 | 
            -
                        'Read': 'read',
         | 
| 2422 | 
            -
                        'Write': 'write',
         | 
| 2423 | 
            -
                        'Edit': 'edit',
         | 
| 2424 | 
            -
                        'MultiEdit': 'edit',
         | 
| 2425 | 
            -
                        'Glob': 'search',
         | 
| 2426 | 
            -
                        'LS': 'list',
         | 
| 2427 | 
            -
                        'NotebookRead': 'read',
         | 
| 2428 | 
            -
                        'NotebookEdit': 'edit',
         | 
| 2429 | 
            -
                        'Grep': 'search'
         | 
| 2430 | 
            -
                    };
         | 
| 2431 | 
            -
                    
         | 
| 2432 | 
            -
                    return operationMap[toolName] || 'operation';
         | 
| 2433 | 
            -
                }
         | 
| 2434 | 
            -
                
         | 
| 2435 | 
            -
                /**
         | 
| 2436 | 
            -
                 * Get file operation type from paired events
         | 
| 2437 | 
            -
                 */
         | 
| 2438 | 
            -
                getFileOperationFromPair(pair) {
         | 
| 2439 | 
            -
                    const toolName = pair.tool_name;
         | 
| 2440 | 
            -
                    const operationMap = {
         | 
| 2441 | 
            -
                        'Read': 'read',
         | 
| 2442 | 
            -
                        'Write': 'write',
         | 
| 2443 | 
            -
                        'Edit': 'edit',
         | 
| 2444 | 
            -
                        'MultiEdit': 'edit',
         | 
| 2445 | 
            -
                        'Glob': 'search',
         | 
| 2446 | 
            -
                        'LS': 'list',
         | 
| 2447 | 
            -
                        'NotebookRead': 'read',
         | 
| 2448 | 
            -
                        'NotebookEdit': 'edit',
         | 
| 2449 | 
            -
                        'Grep': 'search'
         | 
| 2450 | 
            -
                    };
         | 
| 2451 | 
            -
                    
         | 
| 2452 | 
            -
                    return operationMap[toolName] || 'operation';
         | 
| 2453 | 
            -
                }
         | 
| 2454 | 
            -
                
         | 
| 2455 | 
            -
                /**
         | 
| 2456 | 
            -
                 * Extract agent from paired events using inference
         | 
| 2457 | 
            -
                 */
         | 
| 2458 | 
            -
                extractAgentFromPair(pair) {
         | 
| 2459 | 
            -
                    // Try to get inference from either event
         | 
| 2460 | 
            -
                    const preEvent = pair.pre_event;
         | 
| 2461 | 
            -
                    const postEvent = pair.post_event;
         | 
| 2462 | 
            -
                    
         | 
| 2463 | 
            -
                    if (preEvent) {
         | 
| 2464 | 
            -
                        const eventIndex = this.eventViewer.events.indexOf(preEvent);
         | 
| 2465 | 
            -
                        const inference = this.getInferredAgent(eventIndex);
         | 
| 2466 | 
            -
                        if (inference) {
         | 
| 2467 | 
            -
                            return {
         | 
| 2468 | 
            -
                                name: inference.agentName,
         | 
| 2469 | 
            -
                                confidence: inference.confidence
         | 
| 2470 | 
            -
                            };
         | 
| 2471 | 
            -
                        }
         | 
| 2472 | 
            -
                    }
         | 
| 2473 | 
            -
                    
         | 
| 2474 | 
            -
                    if (postEvent) {
         | 
| 2475 | 
            -
                        const eventIndex = this.eventViewer.events.indexOf(postEvent);
         | 
| 2476 | 
            -
                        const inference = this.getInferredAgent(eventIndex);
         | 
| 2477 | 
            -
                        if (inference) {
         | 
| 2478 | 
            -
                            return {
         | 
| 2479 | 
            -
                                name: inference.agentName,
         | 
| 2480 | 
            -
                                confidence: inference.confidence
         | 
| 2481 | 
            -
                            };
         | 
| 2482 | 
            -
                        }
         | 
| 2483 | 
            -
                    }
         | 
| 2484 | 
            -
                    
         | 
| 2485 | 
            -
                    // Fallback to legacy logic
         | 
| 2486 | 
            -
                    const preAgent = preEvent?.agent_type || preEvent?.subagent_type;
         | 
| 2487 | 
            -
                    const postAgent = postEvent?.agent_type || postEvent?.subagent_type;
         | 
| 2488 | 
            -
                    
         | 
| 2489 | 
            -
                    // Prefer non-'main' and non-'unknown' agents
         | 
| 2490 | 
            -
                    if (preAgent && preAgent !== 'main' && preAgent !== 'unknown') {
         | 
| 2491 | 
            -
                        return { name: preAgent, confidence: 'legacy' };
         | 
| 2492 | 
            -
                    }
         | 
| 2493 | 
            -
                    if (postAgent && postAgent !== 'main' && postAgent !== 'unknown') {
         | 
| 2494 | 
            -
                        return { name: postAgent, confidence: 'legacy' };
         | 
| 2495 | 
            -
                    }
         | 
| 2496 | 
            -
                    
         | 
| 2497 | 
            -
                    // Fallback to any agent
         | 
| 2498 | 
            -
                    const agentName = preAgent || postAgent || 'PM';
         | 
| 2499 | 
            -
                    return { name: agentName, confidence: 'fallback' };
         | 
| 2500 | 
            -
                }
         | 
| 2501 | 
            -
             | 
| 2502 | 
            -
                /**
         | 
| 2503 | 
            -
                 * Extract working directory from event pair
         | 
| 2504 | 
            -
                 */
         | 
| 2505 | 
            -
                extractWorkingDirectoryFromPair(pair) {
         | 
| 2506 | 
            -
                    // Try to get working directory from either event's data
         | 
| 2507 | 
            -
                    const preEvent = pair.pre_event;
         | 
| 2508 | 
            -
                    const postEvent = pair.post_event;
         | 
| 2509 | 
            -
                    
         | 
| 2510 | 
            -
                    // Check pre_event first
         | 
| 2511 | 
            -
                    if (preEvent?.data?.working_directory) {
         | 
| 2512 | 
            -
                        return preEvent.data.working_directory;
         | 
| 2513 | 
            -
                    }
         | 
| 2514 | 
            -
                    
         | 
| 2515 | 
            -
                    // Check post_event
         | 
| 2516 | 
            -
                    if (postEvent?.data?.working_directory) {
         | 
| 2517 | 
            -
                        return postEvent.data.working_directory;
         | 
| 2518 | 
            -
                    }
         | 
| 2519 | 
            -
                    
         | 
| 2520 | 
            -
                    // Check tool_parameters for working directory
         | 
| 2521 | 
            -
                    if (preEvent?.tool_parameters?.working_dir) {
         | 
| 2522 | 
            -
                        return preEvent.tool_parameters.working_dir;
         | 
| 2523 | 
            -
                    }
         | 
| 2524 | 
            -
                    
         | 
| 2525 | 
            -
                    if (postEvent?.tool_parameters?.working_dir) {
         | 
| 2526 | 
            -
                        return postEvent.tool_parameters.working_dir;
         | 
| 2527 | 
            -
                    }
         | 
| 2528 | 
            -
                    
         | 
| 2529 | 
            -
                    // Fallback to null (will use default behavior in showGitDiffModal)
         | 
| 2530 | 
            -
                    return null;
         | 
| 2531 | 
            -
                }
         | 
| 2532 | 
            -
             | 
| 2533 | 
            -
                /**
         | 
| 2534 | 
            -
                 * Get file operation details
         | 
| 2535 | 
            -
                 */
         | 
| 2536 | 
            -
                getFileOperationDetails(event) {
         | 
| 2537 | 
            -
                    const toolName = event.tool_name;
         | 
| 2538 | 
            -
                    const params = event.tool_parameters;
         | 
| 2539 | 
            -
                    
         | 
| 2540 | 
            -
                    switch (toolName) {
         | 
| 2541 | 
            -
                        case 'Edit':
         | 
| 2542 | 
            -
                        case 'MultiEdit':
         | 
| 2543 | 
            -
                            return `Modified content`;
         | 
| 2544 | 
            -
                        case 'Write':
         | 
| 2545 | 
            -
                            return `Created/updated file`;
         | 
| 2546 | 
            -
                        case 'Read':
         | 
| 2547 | 
            -
                            return `Read file content`;
         | 
| 2548 | 
            -
                        case 'NotebookRead':
         | 
| 2549 | 
            -
                            return `Read notebook content`;
         | 
| 2550 | 
            -
                        case 'NotebookEdit':
         | 
| 2551 | 
            -
                            return `Modified notebook`;
         | 
| 2552 | 
            -
                        case 'Glob':
         | 
| 2553 | 
            -
                            return `Searched pattern: ${params?.pattern || 'unknown'}`;
         | 
| 2554 | 
            -
                        case 'Grep':
         | 
| 2555 | 
            -
                            return `Searched pattern: ${params?.pattern || 'unknown'}`;
         | 
| 2556 | 
            -
                        case 'LS':
         | 
| 2557 | 
            -
                            return `Listed directory`;
         | 
| 2558 | 
            -
                        default:
         | 
| 2559 | 
            -
                            return '';
         | 
| 2560 | 
            -
                    }
         | 
| 2561 | 
            -
                }
         | 
| 2562 | 
            -
                
         | 
| 2563 | 
            -
                /**
         | 
| 2564 | 
            -
                 * Get file operation details from paired events
         | 
| 2565 | 
            -
                 */
         | 
| 2566 | 
            -
                getFileOperationDetailsFromPair(pair) {
         | 
| 2567 | 
            -
                    const toolName = pair.tool_name;
         | 
| 2568 | 
            -
                    
         | 
| 2569 | 
            -
                    // Get parameters from either event
         | 
| 2570 | 
            -
                    const preParams = pair.pre_event?.tool_parameters || {};
         | 
| 2571 | 
            -
                    const postParams = pair.post_event?.tool_parameters || {};
         | 
| 2572 | 
            -
                    const params = { ...preParams, ...postParams };
         | 
| 2573 | 
            -
                    
         | 
| 2574 | 
            -
                    switch (toolName) {
         | 
| 2575 | 
            -
                        case 'Edit':
         | 
| 2576 | 
            -
                        case 'MultiEdit':
         | 
| 2577 | 
            -
                            return `Modified content`;
         | 
| 2578 | 
            -
                        case 'Write':
         | 
| 2579 | 
            -
                            return `Created/updated file`;
         | 
| 2580 | 
            -
                        case 'Read':
         | 
| 2581 | 
            -
                            return `Read file content`;
         | 
| 2582 | 
            -
                        case 'NotebookRead':
         | 
| 2583 | 
            -
                            return `Read notebook content`;
         | 
| 2584 | 
            -
                        case 'NotebookEdit':
         | 
| 2585 | 
            -
                            return `Modified notebook`;
         | 
| 2586 | 
            -
                        case 'Glob':
         | 
| 2587 | 
            -
                            return `Searched pattern: ${params?.pattern || 'unknown'}`;
         | 
| 2588 | 
            -
                        case 'Grep':
         | 
| 2589 | 
            -
                            return `Searched pattern: ${params?.pattern || 'unknown'}`;
         | 
| 2590 | 
            -
                        case 'LS':
         | 
| 2591 | 
            -
                            return `Listed directory`;
         | 
| 2592 | 
            -
                        default:
         | 
| 2593 | 
            -
                            return '';
         | 
| 2594 | 
            -
                    }
         | 
| 2595 | 
            -
                }
         | 
| 2596 | 
            -
             | 
| 2597 | 
            -
                /**
         | 
| 2598 | 
            -
                 * Get icon for file operations - shows combined icons for read+write
         | 
| 2599 | 
            -
                 */
         | 
| 2600 | 
            -
                getFileOperationIcon(operations) {
         | 
| 2601 | 
            -
                    // Check for notebook operations first
         | 
| 2602 | 
            -
                    const hasNotebook = operations.some(op => op.details && (op.details.includes('notebook') || op.details.includes('Notebook')));
         | 
| 2603 | 
            -
                    if (hasNotebook) return '๐';
         | 
| 2604 | 
            -
                    
         | 
| 2605 | 
            -
                    const hasWrite = operations.some(op => ['write', 'edit'].includes(op.operation));
         | 
| 2606 | 
            -
                    const hasRead = operations.some(op => op.operation === 'read');
         | 
| 2607 | 
            -
                    const hasSearch = operations.some(op => op.operation === 'search');
         | 
| 2608 | 
            -
                    const hasList = operations.some(op => op.operation === 'list');
         | 
| 2609 | 
            -
                    
         | 
| 2610 | 
            -
                    // Show both icons for read+write combinations
         | 
| 2611 | 
            -
                    if (hasWrite && hasRead) return '๐โ๏ธ'; // Both read and write
         | 
| 2612 | 
            -
                    if (hasWrite) return 'โ๏ธ'; // Write only
         | 
| 2613 | 
            -
                    if (hasRead) return '๐'; // Read only
         | 
| 2614 | 
            -
                    if (hasSearch) return '๐'; // Search only
         | 
| 2615 | 
            -
                    if (hasList) return '๐'; // List only
         | 
| 2616 | 
            -
                    return '๐'; // Default
         | 
| 2617 | 
            -
                }
         | 
| 2618 | 
            -
             | 
| 2619 | 
            -
                /**
         | 
| 2620 | 
            -
                 * Get icon for specific operation
         | 
| 2621 | 
            -
                 */
         | 
| 2622 | 
            -
                getOperationIcon(operation) {
         | 
| 2623 | 
            -
                    const icons = {
         | 
| 2624 | 
            -
                        read: '๐',
         | 
| 2625 | 
            -
                        write: '๐',
         | 
| 2626 | 
            -
                        edit: 'โ๏ธ',
         | 
| 2627 | 
            -
                        search: '๐',
         | 
| 2628 | 
            -
                        list: '๐'
         | 
| 2629 | 
            -
                    };
         | 
| 2630 | 
            -
                    return icons[operation] || '๐';
         | 
| 2631 | 
            -
                }
         | 
| 2632 | 
            -
             | 
| 2633 | 
            -
                /**
         | 
| 2634 | 
            -
                 * Format timestamp for display
         | 
| 2635 | 
            -
                 * @param {string|number} timestamp - Timestamp to format
         | 
| 2636 | 
            -
                 * @returns {string} Formatted time
         | 
| 2637 | 
            -
                 */
         | 
| 2638 | 
            -
                formatTimestamp(timestamp) {
         | 
| 2639 | 
            -
                    if (!timestamp) return 'Unknown time';
         | 
| 2640 | 
            -
                    
         | 
| 2641 | 
            -
                    try {
         | 
| 2642 | 
            -
                        const date = new Date(timestamp);
         | 
| 2643 | 
            -
                        return date.toLocaleTimeString('en-US', {
         | 
| 2644 | 
            -
                            hour: 'numeric',
         | 
| 2645 | 
            -
                            minute: '2-digit',
         | 
| 2646 | 
            -
                            second: '2-digit',
         | 
| 2647 | 
            -
                            hour12: true
         | 
| 2648 | 
            -
                        });
         | 
| 2649 | 
            -
                    } catch (e) {
         | 
| 2650 | 
            -
                        return 'Invalid time';
         | 
| 2651 | 
            -
                    }
         | 
| 2652 | 
            -
                }
         | 
| 2653 | 
            -
             | 
| 2654 | 
            -
                /**
         | 
| 2655 | 
            -
                 * Get relative file path for display
         | 
| 2656 | 
            -
                 */
         | 
| 2657 | 
            -
                getRelativeFilePath(filePath) {
         | 
| 2658 | 
            -
                    // Try to make path relative to common base paths
         | 
| 2659 | 
            -
                    const commonPaths = [
         | 
| 2660 | 
            -
                        '/Users/masa/Projects/claude-mpm/',
         | 
| 2661 | 
            -
                        '.'
         | 
| 2662 | 
            -
                    ];
         | 
| 2663 | 
            -
                    
         | 
| 2664 | 
            -
                    for (const basePath of commonPaths) {
         | 
| 2665 | 
            -
                        if (filePath.startsWith(basePath)) {
         | 
| 2666 | 
            -
                            return filePath.substring(basePath.length).replace(/^\//, '');
         | 
| 2667 | 
            -
                        }
         | 
| 2668 | 
            -
                    }
         | 
| 2669 | 
            -
                    
         | 
| 2670 | 
            -
                    // If no common path found, show last 2-3 path segments
         | 
| 2671 | 
            -
                    const parts = filePath.split('/');
         | 
| 2672 | 
            -
                    if (parts.length > 3) {
         | 
| 2673 | 
            -
                        return '.../' + parts.slice(-2).join('/');
         | 
| 2674 | 
            -
                    }
         | 
| 2675 | 
            -
                    
         | 
| 2676 | 
            -
                    return filePath;
         | 
| 2677 | 
            -
                }
         | 
| 2678 | 
            -
             | 
| 2679 | 
            -
                /**
         | 
| 2680 | 
            -
                 * Apply agents tab filtering
         | 
| 2681 | 
            -
                 */
         | 
| 2682 | 
            -
                applyAgentsFilters(events) {
         | 
| 2683 | 
            -
                    const searchInput = document.getElementById('agents-search-input');
         | 
| 2684 | 
            -
                    const typeFilter = document.getElementById('agents-type-filter');
         | 
| 2685 | 
            -
                    
         | 
| 2686 | 
            -
                    const searchText = searchInput ? searchInput.value.toLowerCase() : '';
         | 
| 2687 | 
            -
                    const typeValue = typeFilter ? typeFilter.value : '';
         | 
| 2688 | 
            -
                    
         | 
| 2689 | 
            -
                    return events.filter(event => {
         | 
| 2690 | 
            -
                        // Search filter
         | 
| 2691 | 
            -
                        if (searchText) {
         | 
| 2692 | 
            -
                            const searchableText = [
         | 
| 2693 | 
            -
                                event.subagent_type || '',
         | 
| 2694 | 
            -
                                event.agent_type || '',
         | 
| 2695 | 
            -
                                event.name || '',
         | 
| 2696 | 
            -
                                event.type || '',
         | 
| 2697 | 
            -
                                event.subtype || ''
         | 
| 2698 | 
            -
                            ].join(' ').toLowerCase();
         | 
| 2699 | 
            -
                            
         | 
| 2700 | 
            -
                            if (!searchableText.includes(searchText)) {
         | 
| 2701 | 
            -
                                return false;
         | 
| 2702 | 
            -
                            }
         | 
| 2703 | 
            -
                        }
         | 
| 2704 | 
            -
                        
         | 
| 2705 | 
            -
                        // Type filter
         | 
| 2706 | 
            -
                        if (typeValue) {
         | 
| 2707 | 
            -
                            const agentType = event.subagent_type || event.agent_type || 'unknown';
         | 
| 2708 | 
            -
                            if (!agentType.toLowerCase().includes(typeValue.toLowerCase())) {
         | 
| 2709 | 
            -
                                return false;
         | 
| 2710 | 
            -
                            }
         | 
| 2711 | 
            -
                        }
         | 
| 2712 | 
            -
                        
         | 
| 2713 | 
            -
                        return true;
         | 
| 2714 | 
            -
                    });
         | 
| 2715 | 
            -
                }
         | 
| 2716 | 
            -
             | 
| 2717 | 
            -
                /**
         | 
| 2718 | 
            -
                 * Apply tools tab filtering
         | 
| 2719 | 
            -
                 */
         | 
| 2720 | 
            -
                applyToolsFilters(events) {
         | 
| 2721 | 
            -
                    const searchInput = document.getElementById('tools-search-input');
         | 
| 2722 | 
            -
                    const typeFilter = document.getElementById('tools-type-filter');
         | 
| 2723 | 
            -
                    
         | 
| 2724 | 
            -
                    const searchText = searchInput ? searchInput.value.toLowerCase() : '';
         | 
| 2725 | 
            -
                    const typeValue = typeFilter ? typeFilter.value : '';
         | 
| 2726 | 
            -
                    
         | 
| 2727 | 
            -
                    return events.filter(event => {
         | 
| 2728 | 
            -
                        // Search filter
         | 
| 2729 | 
            -
                        if (searchText) {
         | 
| 2730 | 
            -
                            const searchableText = [
         | 
| 2731 | 
            -
                                event.tool_name || '',
         | 
| 2732 | 
            -
                                event.agent_type || '',
         | 
| 2733 | 
            -
                                event.type || '',
         | 
| 2734 | 
            -
                                event.subtype || ''
         | 
| 2735 | 
            -
                            ].join(' ').toLowerCase();
         | 
| 2736 | 
            -
                            
         | 
| 2737 | 
            -
                            if (!searchableText.includes(searchText)) {
         | 
| 2738 | 
            -
                                return false;
         | 
| 2739 | 
            -
                            }
         | 
| 2740 | 
            -
                        }
         | 
| 2741 | 
            -
                        
         | 
| 2742 | 
            -
                        // Type filter
         | 
| 2743 | 
            -
                        if (typeValue) {
         | 
| 2744 | 
            -
                            const toolName = event.tool_name || '';
         | 
| 2745 | 
            -
                            if (toolName !== typeValue) {
         | 
| 2746 | 
            -
                                return false;
         | 
| 2747 | 
            -
                            }
         | 
| 2748 | 
            -
                        }
         | 
| 2749 | 
            -
                        
         | 
| 2750 | 
            -
                        return true;
         | 
| 2751 | 
            -
                    });
         | 
| 2752 | 
            -
                }
         | 
| 2753 | 
            -
             | 
| 2754 | 
            -
                /**
         | 
| 2755 | 
            -
                 * Apply tools tab filtering for tool calls
         | 
| 2756 | 
            -
                 */
         | 
| 2757 | 
            -
                applyToolCallFilters(toolCallsArray) {
         | 
| 2758 | 
            -
                    const searchInput = document.getElementById('tools-search-input');
         | 
| 2759 | 
            -
                    const typeFilter = document.getElementById('tools-type-filter');
         | 
| 2760 | 
            -
                    
         | 
| 2761 | 
            -
                    const searchText = searchInput ? searchInput.value.toLowerCase() : '';
         | 
| 2762 | 
            -
                    const typeValue = typeFilter ? typeFilter.value : '';
         | 
| 2763 | 
            -
                    
         | 
| 2764 | 
            -
                    return toolCallsArray.filter(([key, toolCall]) => {
         | 
| 2765 | 
            -
                        // Search filter
         | 
| 2766 | 
            -
                        if (searchText) {
         | 
| 2767 | 
            -
                            const searchableText = [
         | 
| 2768 | 
            -
                                toolCall.tool_name || '',
         | 
| 2769 | 
            -
                                toolCall.agent_type || '',
         | 
| 2770 | 
            -
                                'tool_call'
         | 
| 2771 | 
            -
                            ].join(' ').toLowerCase();
         | 
| 2772 | 
            -
                            
         | 
| 2773 | 
            -
                            if (!searchableText.includes(searchText)) {
         | 
| 2774 | 
            -
                                return false;
         | 
| 2775 | 
            -
                            }
         | 
| 2776 | 
            -
                        }
         | 
| 2777 | 
            -
                        
         | 
| 2778 | 
            -
                        // Type filter
         | 
| 2779 | 
            -
                        if (typeValue) {
         | 
| 2780 | 
            -
                            const toolName = toolCall.tool_name || '';
         | 
| 2781 | 
            -
                            if (toolName !== typeValue) {
         | 
| 2782 | 
            -
                                return false;
         | 
| 2783 | 
            -
                            }
         | 
| 2784 | 
            -
                        }
         | 
| 2785 | 
            -
                        
         | 
| 2786 | 
            -
                        return true;
         | 
| 2787 | 
            -
                    });
         | 
| 2788 | 
            -
                }
         | 
| 2789 | 
            -
             | 
| 2790 | 
            -
                /**
         | 
| 2791 | 
            -
                 * Apply files tab filtering
         | 
| 2792 | 
            -
                 */
         | 
| 2793 | 
            -
                applyFilesFilters(fileOperations) {
         | 
| 2794 | 
            -
                    const searchInput = document.getElementById('files-search-input');
         | 
| 2795 | 
            -
                    const typeFilter = document.getElementById('files-type-filter');
         | 
| 2796 | 
            -
                    
         | 
| 2797 | 
            -
                    const searchText = searchInput ? searchInput.value.toLowerCase() : '';
         | 
| 2798 | 
            -
                    const typeValue = typeFilter ? typeFilter.value : '';
         | 
| 2799 | 
            -
                    
         | 
| 2800 | 
            -
                    return fileOperations.filter(([filePath, fileData]) => {
         | 
| 2801 | 
            -
                        // Session filter - filter operations within each file
         | 
| 2802 | 
            -
                        if (this.selectedSessionId) {
         | 
| 2803 | 
            -
                            // Filter operations for this file by session
         | 
| 2804 | 
            -
                            const sessionOperations = fileData.operations.filter(op => 
         | 
| 2805 | 
            -
                                op.sessionId === this.selectedSessionId
         | 
| 2806 | 
            -
                            );
         | 
| 2807 | 
            -
                            
         | 
| 2808 | 
            -
                            // If no operations from this session, exclude the file
         | 
| 2809 | 
            -
                            if (sessionOperations.length === 0) {
         | 
| 2810 | 
            -
                                return false;
         | 
| 2811 | 
            -
                            }
         | 
| 2812 | 
            -
                            
         | 
| 2813 | 
            -
                            // Update the fileData to only include session-specific operations
         | 
| 2814 | 
            -
                            // (Note: This creates a filtered view without modifying the original)
         | 
| 2815 | 
            -
                            fileData = {
         | 
| 2816 | 
            -
                                ...fileData,
         | 
| 2817 | 
            -
                                operations: sessionOperations,
         | 
| 2818 | 
            -
                                lastOperation: sessionOperations[sessionOperations.length - 1]?.timestamp || fileData.lastOperation
         | 
| 2819 | 
            -
                            };
         | 
| 2820 | 
            -
                        }
         | 
| 2821 | 
            -
                        
         | 
| 2822 | 
            -
                        // Search filter
         | 
| 2823 | 
            -
                        if (searchText) {
         | 
| 2824 | 
            -
                            const searchableText = [
         | 
| 2825 | 
            -
                                filePath,
         | 
| 2826 | 
            -
                                ...fileData.operations.map(op => op.operation),
         | 
| 2827 | 
            -
                                ...fileData.operations.map(op => op.agent)
         | 
| 2828 | 
            -
                            ].join(' ').toLowerCase();
         | 
| 2829 | 
            -
                            
         | 
| 2830 | 
            -
                            if (!searchableText.includes(searchText)) {
         | 
| 2831 | 
            -
                                return false;
         | 
| 2832 | 
            -
                            }
         | 
| 2833 | 
            -
                        }
         | 
| 2834 | 
            -
                        
         | 
| 2835 | 
            -
                        // Type filter
         | 
| 2836 | 
            -
                        if (typeValue) {
         | 
| 2837 | 
            -
                            const hasOperationType = fileData.operations.some(op => op.operation === typeValue);
         | 
| 2838 | 
            -
                            if (!hasOperationType) {
         | 
| 2839 | 
            -
                                return false;
         | 
| 2840 | 
            -
                            }
         | 
| 2841 | 
            -
                        }
         | 
| 2842 | 
            -
                        
         | 
| 2843 | 
            -
                        return true;
         | 
| 2844 | 
            -
                    });
         | 
| 2845 | 
            -
                }
         | 
| 2846 | 
            -
             | 
| 2847 | 
            -
                /**
         | 
| 2848 | 
            -
                 * Extract operation from event type
         | 
| 2849 | 
            -
                 */
         | 
| 2850 | 
            -
                extractOperation(eventType) {
         | 
| 2851 | 
            -
                    if (!eventType) return 'unknown';
         | 
| 2852 | 
            -
                    
         | 
| 2853 | 
            -
                    if (eventType.includes('pre_')) return 'pre-' + eventType.split('pre_')[1];
         | 
| 2854 | 
            -
                    if (eventType.includes('post_')) return 'post-' + eventType.split('post_')[1];
         | 
| 2855 | 
            -
                    if (eventType.includes('delegation')) return 'delegation';
         | 
| 2856 | 
            -
                    if (eventType.includes('start')) return 'started';
         | 
| 2857 | 
            -
                    if (eventType.includes('end')) return 'ended';
         | 
| 2858 | 
            -
                    
         | 
| 2859 | 
            -
                    // Extract operation from type like "hook.pre_tool" -> "pre_tool"
         | 
| 2860 | 
            -
                    const parts = eventType.split('.');
         | 
| 2861 | 
            -
                    return parts.length > 1 ? parts[1] : eventType;
         | 
| 2862 | 
            -
                }
         | 
| 2863 | 
            -
             | 
| 2864 | 
            -
                /**
         | 
| 2865 | 
            -
                 * Extract tool name from hook event type
         | 
| 2866 | 
            -
                 */
         | 
| 2867 | 
            -
                extractToolFromHook(eventType) {
         | 
| 2868 | 
            -
                    if (!eventType || !eventType.startsWith('hook.')) return null;
         | 
| 2869 | 
            -
                    
         | 
| 2870 | 
            -
                    // For hook events, the tool name might be in the data
         | 
| 2871 | 
            -
                    return 'Tool'; // Fallback - actual tool name should be in event.tool_name
         | 
| 2872 | 
            -
                }
         | 
| 2873 | 
            -
             | 
| 2874 | 
            -
                /**
         | 
| 2875 | 
            -
                 * Extract tool name from subtype
         | 
| 2876 | 
            -
                 */
         | 
| 2877 | 
            -
                extractToolFromSubtype(subtype) {
         | 
| 2878 | 
            -
                    if (!subtype) return null;
         | 
| 2879 | 
            -
                    
         | 
| 2880 | 
            -
                    // Try to extract tool name from subtype patterns like 'pre_tool' or 'post_tool'
         | 
| 2881 | 
            -
                    if (subtype.includes('tool')) {
         | 
| 2882 | 
            -
                        return 'Tool'; // Generic fallback
         | 
| 2883 | 
            -
                    }
         | 
| 2884 | 
            -
                    
         | 
| 2885 | 
            -
                    return null;
         | 
| 2886 | 
            -
                }
         | 
| 2887 | 
            -
             | 
| 2888 | 
            -
                /**
         | 
| 2889 | 
            -
                 * Extract tool target for display
         | 
| 2890 | 
            -
                 */
         | 
| 2891 | 
            -
                extractToolTarget(toolName, params, toolParameters) {
         | 
| 2892 | 
            -
                    const allParams = { ...params, ...toolParameters };
         | 
| 2893 | 
            -
                    
         | 
| 2894 | 
            -
                    switch (toolName) {
         | 
| 2895 | 
            -
                        case 'Read':
         | 
| 2896 | 
            -
                        case 'Write':
         | 
| 2897 | 
            -
                        case 'Edit':
         | 
| 2898 | 
            -
                        case 'MultiEdit':
         | 
| 2899 | 
            -
                            return allParams.file_path || 'Unknown file';
         | 
| 2900 | 
            -
                        case 'Bash':
         | 
| 2901 | 
            -
                            return allParams.command || 'Unknown command';
         | 
| 2902 | 
            -
                        case 'Glob':
         | 
| 2903 | 
            -
                            return allParams.pattern || 'Unknown pattern';
         | 
| 2904 | 
            -
                        case 'Grep':
         | 
| 2905 | 
            -
                            return `"${allParams.pattern || 'unknown'}" in ${allParams.path || 'unknown path'}`;
         | 
| 2906 | 
            -
                        case 'LS':
         | 
| 2907 | 
            -
                            return allParams.path || 'Unknown path';
         | 
| 2908 | 
            -
                        default:
         | 
| 2909 | 
            -
                            if (Object.keys(allParams).length > 0) {
         | 
| 2910 | 
            -
                                return JSON.stringify(allParams).substring(0, 50) + '...';
         | 
| 2911 | 
            -
                            }
         | 
| 2912 | 
            -
                            return 'No parameters';
         | 
| 2913 | 
            -
                    }
         | 
| 2914 | 
            -
                }
         | 
| 2915 | 
            -
             | 
| 2916 | 
            -
                /**
         | 
| 2917 | 
            -
                 * Get filtered events for a specific tab
         | 
| 2918 | 
            -
                 */
         | 
| 2919 | 
            -
                getFilteredEventsForTab(tabName) {
         | 
| 2920 | 
            -
                    // Use ALL events, not the EventViewer's filtered events
         | 
| 2921 | 
            -
                    // Each tab will apply its own filtering logic
         | 
| 2922 | 
            -
                    const events = this.eventViewer.events;
         | 
| 2923 | 
            -
                    console.log(`getFilteredEventsForTab(${tabName}) - using RAW events: ${events.length} total`);
         | 
| 2924 | 
            -
                    
         | 
| 2925 | 
            -
                    // Enhanced debugging for empty events
         | 
| 2926 | 
            -
                    if (events.length === 0) {
         | 
| 2927 | 
            -
                        console.log(`โ NO RAW EVENTS available!`);
         | 
| 2928 | 
            -
                        console.log('EventViewer state:', {
         | 
| 2929 | 
            -
                            total_events: this.eventViewer.events.length,
         | 
| 2930 | 
            -
                            filtered_events: this.eventViewer.filteredEvents.length,
         | 
| 2931 | 
            -
                            search_filter: this.eventViewer.searchFilter,
         | 
| 2932 | 
            -
                            type_filter: this.eventViewer.typeFilter,
         | 
| 2933 | 
            -
                            session_filter: this.eventViewer.sessionFilter
         | 
| 2934 | 
            -
                        });
         | 
| 2935 | 
            -
                    } else {
         | 
| 2936 | 
            -
                        console.log('โ
 Raw events available for', tabName, '- sample:', events[0]);
         | 
| 2937 | 
            -
                        console.log('EventViewer filters (IGNORED for tabs):', {
         | 
| 2938 | 
            -
                            search_filter: this.eventViewer.searchFilter,
         | 
| 2939 | 
            -
                            type_filter: this.eventViewer.typeFilter,
         | 
| 2940 | 
            -
                            session_filter: this.eventViewer.sessionFilter
         | 
| 2941 | 
            -
                        });
         | 
| 2942 | 
            -
                    }
         | 
| 2943 | 
            -
                    
         | 
| 2944 | 
            -
                    return events;
         | 
| 2945 | 
            -
                }
         | 
| 2946 | 
            -
             | 
| 2947 | 
            -
                /**
         | 
| 2948 | 
            -
                 * Scroll a list container to the bottom
         | 
| 2949 | 
            -
                 * @param {string} listId - The ID of the list container element
         | 
| 2950 | 
            -
                 */
         | 
| 2951 | 
            -
                scrollListToBottom(listId) {
         | 
| 2952 | 
            -
                    console.log(`[DEBUG] scrollListToBottom called with listId: ${listId}`);
         | 
| 2953 | 
            -
                    
         | 
| 2954 | 
            -
                    // Use setTimeout to ensure DOM updates are completed
         | 
| 2955 | 
            -
                    setTimeout(() => {
         | 
| 2956 | 
            -
                        const listElement = document.getElementById(listId);
         | 
| 2957 | 
            -
                        console.log(`[DEBUG] Element found for ${listId}:`, listElement);
         | 
| 2958 | 
            -
                        
         | 
| 2959 | 
            -
                        if (listElement) {
         | 
| 2960 | 
            -
                            const scrollHeight = listElement.scrollHeight;
         | 
| 2961 | 
            -
                            const clientHeight = listElement.clientHeight;
         | 
| 2962 | 
            -
                            const currentScrollTop = listElement.scrollTop;
         | 
| 2963 | 
            -
                            const computedStyle = window.getComputedStyle(listElement);
         | 
| 2964 | 
            -
                            
         | 
| 2965 | 
            -
                            console.log(`[DEBUG] Scroll metrics for ${listId}:`);
         | 
| 2966 | 
            -
                            console.log(`  - scrollHeight: ${scrollHeight}`);
         | 
| 2967 | 
            -
                            console.log(`  - clientHeight: ${clientHeight}`);
         | 
| 2968 | 
            -
                            console.log(`  - currentScrollTop: ${currentScrollTop}`);
         | 
| 2969 | 
            -
                            console.log(`  - needsScroll: ${scrollHeight > clientHeight}`);
         | 
| 2970 | 
            -
                            console.log(`  - overflowY: ${computedStyle.overflowY}`);
         | 
| 2971 | 
            -
                            console.log(`  - height: ${computedStyle.height}`);
         | 
| 2972 | 
            -
                            console.log(`  - maxHeight: ${computedStyle.maxHeight}`);
         | 
| 2973 | 
            -
                            
         | 
| 2974 | 
            -
                            if (scrollHeight > clientHeight) {
         | 
| 2975 | 
            -
                                // Method 1: Direct scrollTop assignment
         | 
| 2976 | 
            -
                                listElement.scrollTop = scrollHeight;
         | 
| 2977 | 
            -
                                console.log(`[DEBUG] Method 1 - Scroll applied to ${listId}, new scrollTop: ${listElement.scrollTop}`);
         | 
| 2978 | 
            -
                                
         | 
| 2979 | 
            -
                                // Method 2: Force layout recalculation and try again if needed
         | 
| 2980 | 
            -
                                if (listElement.scrollTop !== scrollHeight) {
         | 
| 2981 | 
            -
                                    console.log(`[DEBUG] Method 1 failed, trying method 2 with layout recalculation`);
         | 
| 2982 | 
            -
                                    listElement.offsetHeight; // Force reflow
         | 
| 2983 | 
            -
                                    listElement.scrollTop = scrollHeight;
         | 
| 2984 | 
            -
                                    console.log(`[DEBUG] Method 2 - new scrollTop: ${listElement.scrollTop}`);
         | 
| 2985 | 
            -
                                }
         | 
| 2986 | 
            -
                                
         | 
| 2987 | 
            -
                                // Method 3: scrollIntoView on last element if still not working
         | 
| 2988 | 
            -
                                if (listElement.scrollTop < scrollHeight - clientHeight - 10) {
         | 
| 2989 | 
            -
                                    console.log(`[DEBUG] Methods 1-2 failed, trying method 3 with scrollIntoView`);
         | 
| 2990 | 
            -
                                    const lastElement = listElement.lastElementChild;
         | 
| 2991 | 
            -
                                    if (lastElement) {
         | 
| 2992 | 
            -
                                        lastElement.scrollIntoView({ behavior: 'instant', block: 'end' });
         | 
| 2993 | 
            -
                                        console.log(`[DEBUG] Method 3 - scrollIntoView applied on last element`);
         | 
| 2994 | 
            -
                                    }
         | 
| 2995 | 
            -
                                }
         | 
| 2996 | 
            -
                            } else {
         | 
| 2997 | 
            -
                                console.log(`[DEBUG] No scroll needed for ${listId} - content fits in container`);
         | 
| 2998 | 
            -
                            }
         | 
| 2999 | 
            -
                        } else {
         | 
| 3000 | 
            -
                            console.error(`[DEBUG] Element not found for ID: ${listId}`);
         | 
| 3001 | 
            -
                            // Log all elements with similar IDs to help debug
         | 
| 3002 | 
            -
                            const allElements = document.querySelectorAll('[id*="list"]');
         | 
| 3003 | 
            -
                            console.log(`[DEBUG] Available elements with 'list' in ID:`, Array.from(allElements).map(el => el.id));
         | 
| 3004 | 
            -
                        }
         | 
| 3005 | 
            -
                    }, 50);  // Small delay to ensure content is rendered
         | 
| 3006 | 
            -
                }
         | 
| 3007 | 
            -
             | 
| 3008 | 
            -
                /**
         | 
| 3009 | 
            -
                 * Test scroll functionality - adds dummy content and tries to scroll
         | 
| 3010 | 
            -
                 * This is for debugging purposes only
         | 
| 3011 | 
            -
                 * @returns {Promise<string>} Status message indicating test results
         | 
| 3012 | 
            -
                 */
         | 
| 3013 | 
            -
                async testScrollFunctionality() {
         | 
| 3014 | 
            -
                    console.log('[DEBUG] Testing scroll functionality...');
         | 
| 3015 | 
            -
                    const results = [];
         | 
| 3016 | 
            -
                    const listId = `${this.currentTab}-list`;
         | 
| 3017 | 
            -
                    const listElement = document.getElementById(listId);
         | 
| 3018 | 
            -
                    
         | 
| 3019 | 
            -
                    if (!listElement) {
         | 
| 3020 | 
            -
                        const errorMsg = `โ Could not find list element: ${listId}`;
         | 
| 3021 | 
            -
                        console.error(`[DEBUG] ${errorMsg}`);
         | 
| 3022 | 
            -
                        
         | 
| 3023 | 
            -
                        // Debug: Show available elements
         | 
| 3024 | 
            -
                        const allElements = document.querySelectorAll('[id*="list"]');
         | 
| 3025 | 
            -
                        const availableIds = Array.from(allElements).map(el => el.id);
         | 
| 3026 | 
            -
                        console.log(`[DEBUG] Available elements with 'list' in ID:`, availableIds);
         | 
| 3027 | 
            -
                        results.push(errorMsg);
         | 
| 3028 | 
            -
                        results.push(`Available list IDs: ${availableIds.join(', ')}`);
         | 
| 3029 | 
            -
                        return results.join('\n');
         | 
| 3030 | 
            -
                    }
         | 
| 3031 | 
            -
                    
         | 
| 3032 | 
            -
                    // Get initial state
         | 
| 3033 | 
            -
                    const initialScrollTop = listElement.scrollTop;
         | 
| 3034 | 
            -
                    const initialScrollHeight = listElement.scrollHeight;
         | 
| 3035 | 
            -
                    const initialClientHeight = listElement.clientHeight;
         | 
| 3036 | 
            -
                    const computedStyle = window.getComputedStyle(listElement);
         | 
| 3037 | 
            -
                    
         | 
| 3038 | 
            -
                    results.push(`โ
 Found list element: ${listId}`);
         | 
| 3039 | 
            -
                    results.push(`๐ Initial state: scrollTop=${initialScrollTop}, scrollHeight=${initialScrollHeight}, clientHeight=${initialClientHeight}`);
         | 
| 3040 | 
            -
                    results.push(`๐จ CSS: overflowY=${computedStyle.overflowY}, height=${computedStyle.height}, maxHeight=${computedStyle.maxHeight}`);
         | 
| 3041 | 
            -
                    
         | 
| 3042 | 
            -
                    // Test 1: Direct scroll without adding content
         | 
| 3043 | 
            -
                    console.log('[DEBUG] Test 1: Direct scroll test');
         | 
| 3044 | 
            -
                    listElement.scrollTop = 999999;
         | 
| 3045 | 
            -
                    await new Promise(resolve => setTimeout(resolve, 50));
         | 
| 3046 | 
            -
                    const directScrollResult = listElement.scrollTop;
         | 
| 3047 | 
            -
                    results.push(`๐งช Test 1 - Direct scroll to 999999: scrollTop=${directScrollResult} ${directScrollResult > 0 ? 'โ
' : 'โ'}`);
         | 
| 3048 | 
            -
                    
         | 
| 3049 | 
            -
                    // Reset scroll position
         | 
| 3050 | 
            -
                    listElement.scrollTop = 0;
         | 
| 3051 | 
            -
                    
         | 
| 3052 | 
            -
                    // Test 2: Add visible content to force scrolling need
         | 
| 3053 | 
            -
                    console.log('[DEBUG] Test 2: Adding test content');
         | 
| 3054 | 
            -
                    const originalContent = listElement.innerHTML;
         | 
| 3055 | 
            -
                    
         | 
| 3056 | 
            -
                    // Add 15 large test items to definitely exceed container height
         | 
| 3057 | 
            -
                    for (let i = 0; i < 15; i++) {
         | 
| 3058 | 
            -
                        const testItem = document.createElement('div');
         | 
| 3059 | 
            -
                        testItem.className = 'event-item test-scroll-item';
         | 
| 3060 | 
            -
                        testItem.style.cssText = 'background: #fffacd !important; border: 2px solid #f39c12 !important; padding: 20px; margin: 10px 0; min-height: 60px;';
         | 
| 3061 | 
            -
                        testItem.innerHTML = `<strong>๐งช Test Item ${i + 1}</strong><br>This is a test item to verify scrolling functionality.<br><em>Item height: ~80px total</em>`;
         | 
| 3062 | 
            -
                        listElement.appendChild(testItem);
         | 
| 3063 | 
            -
                    }
         | 
| 3064 | 
            -
                    
         | 
| 3065 | 
            -
                    // Force layout recalculation
         | 
| 3066 | 
            -
                    listElement.offsetHeight;
         | 
| 3067 | 
            -
                    
         | 
| 3068 | 
            -
                    const afterContentScrollHeight = listElement.scrollHeight;
         | 
| 3069 | 
            -
                    const afterContentClientHeight = listElement.clientHeight;
         | 
| 3070 | 
            -
                    const needsScroll = afterContentScrollHeight > afterContentClientHeight;
         | 
| 3071 | 
            -
                    
         | 
| 3072 | 
            -
                    results.push(`๐ฆ Added 15 test items (expected ~1200px total height)`);
         | 
| 3073 | 
            -
                    results.push(`๐ After content: scrollHeight=${afterContentScrollHeight}, clientHeight=${afterContentClientHeight}`);
         | 
| 3074 | 
            -
                    results.push(`๐ Needs scroll: ${needsScroll ? 'YES โ
' : 'NO โ'}`);
         | 
| 3075 | 
            -
                    
         | 
| 3076 | 
            -
                    if (needsScroll) {
         | 
| 3077 | 
            -
                        // Test 3: Multiple scroll methods
         | 
| 3078 | 
            -
                        console.log('[DEBUG] Test 3: Testing different scroll methods');
         | 
| 3079 | 
            -
                        
         | 
| 3080 | 
            -
                        // Method A: Direct scrollTop assignment
         | 
| 3081 | 
            -
                        listElement.scrollTop = afterContentScrollHeight;
         | 
| 3082 | 
            -
                        await new Promise(resolve => setTimeout(resolve, 50));
         | 
| 3083 | 
            -
                        const methodAResult = listElement.scrollTop;
         | 
| 3084 | 
            -
                        const methodASuccess = methodAResult > (afterContentScrollHeight - afterContentClientHeight - 50);
         | 
| 3085 | 
            -
                        results.push(`๐ง Method A - scrollTop assignment: ${methodAResult} ${methodASuccess ? 'โ
' : 'โ'}`);
         | 
| 3086 | 
            -
                        
         | 
| 3087 | 
            -
                        // Method B: scrollIntoView on last element
         | 
| 3088 | 
            -
                        const lastElement = listElement.lastElementChild;
         | 
| 3089 | 
            -
                        if (lastElement) {
         | 
| 3090 | 
            -
                            lastElement.scrollIntoView({ behavior: 'instant', block: 'end' });
         | 
| 3091 | 
            -
                            await new Promise(resolve => setTimeout(resolve, 50));
         | 
| 3092 | 
            -
                            const methodBResult = listElement.scrollTop;
         | 
| 3093 | 
            -
                            const methodBSuccess = methodBResult > (afterContentScrollHeight - afterContentClientHeight - 50);
         | 
| 3094 | 
            -
                            results.push(`๐ง Method B - scrollIntoView: ${methodBResult} ${methodBSuccess ? 'โ
' : 'โ'}`);
         | 
| 3095 | 
            -
                        }
         | 
| 3096 | 
            -
                        
         | 
| 3097 | 
            -
                        // Method C: Using the existing scrollListToBottom method
         | 
| 3098 | 
            -
                        console.log('[DEBUG] Test 4: Using scrollListToBottom method');
         | 
| 3099 | 
            -
                        this.scrollListToBottom(listId);
         | 
| 3100 | 
            -
                        await new Promise(resolve => setTimeout(resolve, 100));
         | 
| 3101 | 
            -
                        const methodCResult = listElement.scrollTop;
         | 
| 3102 | 
            -
                        const methodCSuccess = methodCResult > (afterContentScrollHeight - afterContentClientHeight - 50);
         | 
| 3103 | 
            -
                        results.push(`๐ง Method C - scrollListToBottom: ${methodCResult} ${methodCSuccess ? 'โ
' : 'โ'}`);
         | 
| 3104 | 
            -
                    }
         | 
| 3105 | 
            -
                    
         | 
| 3106 | 
            -
                    // Clean up test content after a visible delay
         | 
| 3107 | 
            -
                    setTimeout(() => {
         | 
| 3108 | 
            -
                        console.log('[DEBUG] Cleaning up test content...');
         | 
| 3109 | 
            -
                        const testItems = listElement.querySelectorAll('.test-scroll-item');
         | 
| 3110 | 
            -
                        testItems.forEach(item => item.remove());
         | 
| 3111 | 
            -
                        results.push(`๐งน Cleaned up ${testItems.length} test items`);
         | 
| 3112 | 
            -
                    }, 2000);
         | 
| 3113 | 
            -
                    
         | 
| 3114 | 
            -
                    const finalResult = results.join('\n');
         | 
| 3115 | 
            -
                    console.log('[DEBUG] Test complete. Results:', finalResult);
         | 
| 3116 | 
            -
                    return finalResult;
         | 
| 3117 | 
            -
                }
         | 
| 3118 | 
            -
             | 
| 3119 | 
            -
                /**
         | 
| 3120 | 
            -
                 * Update connection status in UI
         | 
| 3121 | 
            -
                 */
         | 
| 3122 | 
            -
                updateConnectionStatus(status, type) {
         | 
| 3123 | 
            -
                    const statusElement = document.getElementById('connection-status');
         | 
| 3124 | 
            -
                    if (statusElement) {
         | 
| 3125 | 
            -
                        statusElement.textContent = status;
         | 
| 3126 | 
            -
                        statusElement.className = `status-badge status-${type}`;
         | 
| 3127 | 
            -
                        
         | 
| 3128 | 
            -
                        // Update status indicator
         | 
| 3129 | 
            -
                        const indicator = statusElement.querySelector('span');
         | 
| 3130 | 
            -
                        if (indicator) {
         | 
| 3131 | 
            -
                            indicator.textContent = type === 'connected' ? 'โ' : 'โ';
         | 
| 3132 | 
            -
                        }
         | 
| 3133 | 
            -
                    }
         | 
| 3134 | 
            -
                }
         | 
| 3135 | 
            -
             | 
| 3136 | 
            -
                /**
         | 
| 3137 | 
            -
                 * Clear all events
         | 
| 3138 | 
            -
                 */
         | 
| 3139 | 
            -
                clearEvents() {
         | 
| 3140 | 
            -
                    this.eventViewer.clearEvents();
         | 
| 3141 | 
            -
                    this.fileOperations.clear();
         | 
| 3142 | 
            -
                    this.toolCalls.clear();
         | 
| 3143 | 
            -
                    this.agentEvents = [];
         | 
| 3144 | 
            -
                    this.renderCurrentTab();
         | 
| 3145 | 
            -
                }
         | 
| 3146 | 
            -
             | 
| 3147 | 
            -
                /**
         | 
| 3148 | 
            -
                 * Export current events
         | 
| 3149 | 
            -
                 */
         | 
| 3150 | 
            -
                exportEvents() {
         | 
| 3151 | 
            -
                    this.eventViewer.exportEvents();
         | 
| 3152 | 
            -
                }
         | 
| 3153 | 
            -
             | 
| 3154 | 
            -
                /**
         | 
| 3155 | 
            -
                 * Toggle connection controls visibility
         | 
| 3156 | 
            -
                 */
         | 
| 3157 | 
            -
                toggleConnectionControls() {
         | 
| 3158 | 
            -
                    const controlsRow = document.getElementById('connection-controls-row');
         | 
| 3159 | 
            -
                    const toggleBtn = document.getElementById('connection-toggle-btn');
         | 
| 3160 | 
            -
                    
         | 
| 3161 | 
            -
                    if (controlsRow && toggleBtn) {
         | 
| 3162 | 
            -
                        const isVisible = controlsRow.classList.contains('show');
         | 
| 3163 | 
            -
                        
         | 
| 3164 | 
            -
                        if (isVisible) {
         | 
| 3165 | 
            -
                            controlsRow.classList.remove('show');
         | 
| 3166 | 
            -
                            controlsRow.style.display = 'none';
         | 
| 3167 | 
            -
                            toggleBtn.textContent = 'Connection Settings';
         | 
| 3168 | 
            -
                        } else {
         | 
| 3169 | 
            -
                            controlsRow.style.display = 'flex';
         | 
| 3170 | 
            -
                            // Use setTimeout to ensure display change is applied before adding animation class
         | 
| 3171 | 
            -
                            setTimeout(() => {
         | 
| 3172 | 
            -
                                controlsRow.classList.add('show');
         | 
| 3173 | 
            -
                            }, 10);
         | 
| 3174 | 
            -
                            toggleBtn.textContent = 'Hide Connection Settings';
         | 
| 3175 | 
            -
                        }
         | 
| 3176 | 
            -
                    }
         | 
| 3177 | 
            -
                }
         | 
| 3178 | 
            -
             | 
| 3179 | 
            -
                /**
         | 
| 3180 | 
            -
                 * Clear current selection
         | 
| 3181 | 
            -
                 */
         | 
| 3182 | 
            -
                clearSelection() {
         | 
| 3183 | 
            -
                    this.clearCardSelection();
         | 
| 3184 | 
            -
                    this.eventViewer.clearSelection();
         | 
| 3185 | 
            -
                    this.moduleViewer.clear();
         | 
| 3186 | 
            -
                }
         | 
| 3187 | 
            -
                
         | 
| 3188 | 
            -
                /**
         | 
| 3189 | 
            -
                 * Show dialog to change working directory
         | 
| 3190 | 
            -
                 */
         | 
| 3191 | 
            -
                showChangeDirDialog() {
         | 
| 3192 | 
            -
                    const currentDir = this.currentWorkingDir || this.getDefaultWorkingDir();
         | 
| 3193 | 
            -
                    const newDir = prompt('Enter new working directory:', currentDir);
         | 
| 3194 | 
            -
                    
         | 
| 3195 | 
            -
                    if (newDir && newDir !== currentDir) {
         | 
| 3196 | 
            -
                        this.setWorkingDirectory(newDir);
         | 
| 3197 | 
            -
                    }
         | 
| 3198 | 
            -
                }
         | 
| 3199 | 
            -
                
         | 
| 3200 | 
            -
                /**
         | 
| 3201 | 
            -
                 * Set the working directory for the current session
         | 
| 3202 | 
            -
                 * @param {string} dir - New working directory path
         | 
| 3203 | 
            -
                 */
         | 
| 3204 | 
            -
                setWorkingDirectory(dir) {
         | 
| 3205 | 
            -
                    this.currentWorkingDir = dir;
         | 
| 3206 | 
            -
                    
         | 
| 3207 | 
            -
                    // Update UI
         | 
| 3208 | 
            -
                    const pathElement = document.getElementById('working-dir-path');
         | 
| 3209 | 
            -
                    if (pathElement) {
         | 
| 3210 | 
            -
                        pathElement.textContent = dir;
         | 
| 3211 | 
            -
                        pathElement.title = `Click to change from ${dir}`;
         | 
| 3212 | 
            -
                    }
         | 
| 3213 | 
            -
                    
         | 
| 3214 | 
            -
                    // Update footer (with flag to prevent observer loop)
         | 
| 3215 | 
            -
                    const footerDir = document.getElementById('footer-working-dir');
         | 
| 3216 | 
            -
                    if (footerDir) {
         | 
| 3217 | 
            -
                        this._updatingFooter = true;
         | 
| 3218 | 
            -
                        footerDir.textContent = dir;
         | 
| 3219 | 
            -
                        // Reset flag after a small delay to ensure observer has processed
         | 
| 3220 | 
            -
                        setTimeout(() => {
         | 
| 3221 | 
            -
                            this._updatingFooter = false;
         | 
| 3222 | 
            -
                        }, 10);
         | 
| 3223 | 
            -
                    }
         | 
| 3224 | 
            -
                    
         | 
| 3225 | 
            -
                    // Store in session data if a session is selected
         | 
| 3226 | 
            -
                    const sessionSelect = document.getElementById('session-select');
         | 
| 3227 | 
            -
                    if (sessionSelect && sessionSelect.value) {
         | 
| 3228 | 
            -
                        const sessionId = sessionSelect.value;
         | 
| 3229 | 
            -
                        // Store working directory per session in localStorage
         | 
| 3230 | 
            -
                        const sessionDirs = JSON.parse(localStorage.getItem('sessionWorkingDirs') || '{}');
         | 
| 3231 | 
            -
                        sessionDirs[sessionId] = dir;
         | 
| 3232 | 
            -
                        localStorage.setItem('sessionWorkingDirs', JSON.stringify(sessionDirs));
         | 
| 3233 | 
            -
                    }
         | 
| 3234 | 
            -
                    
         | 
| 3235 | 
            -
                    console.log(`Working directory set to: ${dir}`);
         | 
| 3236 | 
            -
                    
         | 
| 3237 | 
            -
                    // Request git branch for the new directory
         | 
| 3238 | 
            -
                    this.updateGitBranch(dir);
         | 
| 3239 | 
            -
                }
         | 
| 3240 | 
            -
                
         | 
| 3241 | 
            -
                /**
         | 
| 3242 | 
            -
                 * Update git branch display for current working directory
         | 
| 3243 | 
            -
                 * @param {string} dir - Working directory path
         | 
| 3244 | 
            -
                 */
         | 
| 3245 | 
            -
                updateGitBranch(dir) {
         | 
| 3246 | 
            -
                    if (!this.socketClient || !this.socketClient.socket || !this.socketClient.socket.connected) {
         | 
| 3247 | 
            -
                        // Not connected, set to unknown
         | 
| 3248 | 
            -
                        const footerBranch = document.getElementById('footer-git-branch');
         | 
| 3249 | 
            -
                        if (footerBranch) {
         | 
| 3250 | 
            -
                            footerBranch.textContent = 'Not Connected';
         | 
| 3251 | 
            -
                        }
         | 
| 3252 | 
            -
                        return;
         | 
| 3253 | 
            -
                    }
         | 
| 3254 | 
            -
                    
         | 
| 3255 | 
            -
                    // Request git branch from server
         | 
| 3256 | 
            -
                    this.socketClient.socket.emit('get_git_branch', dir);
         | 
| 3257 | 
            -
                }
         | 
| 3258 | 
            -
                
         | 
| 3259 | 
            -
                /**
         | 
| 3260 | 
            -
                 * Get default working directory
         | 
| 3261 | 
            -
                 */
         | 
| 3262 | 
            -
                getDefaultWorkingDir() {
         | 
| 3263 | 
            -
                    // Try to get from footer first (may be set by server)
         | 
| 3264 | 
            -
                    const footerDir = document.getElementById('footer-working-dir');
         | 
| 3265 | 
            -
                    if (footerDir && footerDir.textContent && footerDir.textContent !== 'Unknown') {
         | 
| 3266 | 
            -
                        return footerDir.textContent;
         | 
| 3267 | 
            -
                    }
         | 
| 3268 | 
            -
                    // Fallback to hardcoded default
         | 
| 3269 | 
            -
                    return '/Users/masa/Projects/claude-mpm';
         | 
| 3270 | 
            -
                }
         | 
| 3271 | 
            -
                
         | 
| 3272 | 
            -
                /**
         | 
| 3273 | 
            -
                 * Initialize working directory on dashboard load
         | 
| 3274 | 
            -
                 */
         | 
| 3275 | 
            -
                initializeWorkingDirectory() {
         | 
| 3276 | 
            -
                    // Check if there's a selected session
         | 
| 3277 | 
            -
                    const sessionSelect = document.getElementById('session-select');
         | 
| 3278 | 
            -
                    if (sessionSelect && sessionSelect.value) {
         | 
| 3279 | 
            -
                        // Load working directory for selected session
         | 
| 3280 | 
            -
                        this.loadWorkingDirectoryForSession(sessionSelect.value);
         | 
| 3281 | 
            -
                    } else {
         | 
| 3282 | 
            -
                        // Set default working directory
         | 
| 3283 | 
            -
                        this.setWorkingDirectory(this.getDefaultWorkingDir());
         | 
| 3284 | 
            -
                    }
         | 
| 3285 | 
            -
                }
         | 
| 3286 | 
            -
                
         | 
| 3287 | 
            -
                /**
         | 
| 3288 | 
            -
                 * Watch footer directory for changes and sync working directory
         | 
| 3289 | 
            -
                 */
         | 
| 3290 | 
            -
                watchFooterDirectory() {
         | 
| 3291 | 
            -
                    const footerDir = document.getElementById('footer-working-dir');
         | 
| 3292 | 
            -
                    if (!footerDir) return;
         | 
| 3293 | 
            -
                    
         | 
| 3294 | 
            -
                    // Store observer reference for later use
         | 
| 3295 | 
            -
                    this.footerDirObserver = new MutationObserver((mutations) => {
         | 
| 3296 | 
            -
                        // Skip if we're updating from setWorkingDirectory
         | 
| 3297 | 
            -
                        if (this._updatingFooter) return;
         | 
| 3298 | 
            -
                        
         | 
| 3299 | 
            -
                        mutations.forEach((mutation) => {
         | 
| 3300 | 
            -
                            if (mutation.type === 'childList' || mutation.type === 'characterData') {
         | 
| 3301 | 
            -
                                const newDir = footerDir.textContent.trim();
         | 
| 3302 | 
            -
                                // Only update if it's a valid directory path and different from current
         | 
| 3303 | 
            -
                                if (newDir && 
         | 
| 3304 | 
            -
                                    newDir !== 'Unknown' && 
         | 
| 3305 | 
            -
                                    newDir !== 'Not Connected' &&
         | 
| 3306 | 
            -
                                    newDir.startsWith('/') &&
         | 
| 3307 | 
            -
                                    newDir !== this.currentWorkingDir) {
         | 
| 3308 | 
            -
                                    console.log(`Footer directory changed to: ${newDir}, syncing working directory`);
         | 
| 3309 | 
            -
                                    this.setWorkingDirectory(newDir);
         | 
| 3310 | 
            -
                                }
         | 
| 3311 | 
            -
                            }
         | 
| 3312 | 
            -
                        });
         | 
| 3313 | 
            -
                    });
         | 
| 3314 | 
            -
                    
         | 
| 3315 | 
            -
                    // Start observing
         | 
| 3316 | 
            -
                    this.footerDirObserver.observe(footerDir, {
         | 
| 3317 | 
            -
                        childList: true,
         | 
| 3318 | 
            -
                        characterData: true,
         | 
| 3319 | 
            -
                        subtree: true
         | 
| 3320 | 
            -
                    });
         | 
| 3321 | 
            -
                }
         | 
| 3322 | 
            -
                
         | 
| 3323 | 
            -
                /**
         | 
| 3324 | 
            -
                 * Load working directory for a session
         | 
| 3325 | 
            -
                 * @param {string} sessionId - Session ID
         | 
| 3326 | 
            -
                 */
         | 
| 3327 | 
            -
                loadWorkingDirectoryForSession(sessionId) {
         | 
| 3328 | 
            -
                    if (!sessionId) {
         | 
| 3329 | 
            -
                        // No session selected, use default
         | 
| 3330 | 
            -
                        this.setWorkingDirectory(this.getDefaultWorkingDir());
         | 
| 3331 | 
            -
                        return;
         | 
| 3332 | 
            -
                    }
         | 
| 3333 | 
            -
                    
         | 
| 3334 | 
            -
                    // Load from localStorage
         | 
| 3335 | 
            -
                    const sessionDirs = JSON.parse(localStorage.getItem('sessionWorkingDirs') || '{}');
         | 
| 3336 | 
            -
                    const dir = sessionDirs[sessionId] || this.getDefaultWorkingDir();
         | 
| 3337 | 
            -
                    this.setWorkingDirectory(dir);
         | 
| 3338 | 
            -
                }
         | 
| 3339 | 
            -
             | 
| 3340 | 
            -
                /**
         | 
| 3341 | 
            -
                 * Toggle HUD visualizer mode
         | 
| 3342 | 
            -
                 */
         | 
| 3343 | 
            -
                toggleHUD() {
         | 
| 3344 | 
            -
                    if (!this.isSessionSelected()) {
         | 
| 3345 | 
            -
                        console.log('Cannot toggle HUD: No session selected');
         | 
| 3346 | 
            -
                        return;
         | 
| 3347 | 
            -
                    }
         | 
| 3348 | 
            -
             | 
| 3349 | 
            -
                    this.hudMode = !this.hudMode;
         | 
| 3350 | 
            -
                    this.updateHUDDisplay();
         | 
| 3351 | 
            -
                    
         | 
| 3352 | 
            -
                    console.log('HUD mode toggled:', this.hudMode ? 'ON' : 'OFF');
         | 
| 3353 | 
            -
                }
         | 
| 3354 | 
            -
             | 
| 3355 | 
            -
                /**
         | 
| 3356 | 
            -
                 * Check if a session is currently selected
         | 
| 3357 | 
            -
                 * @returns {boolean} - True if session is selected
         | 
| 3358 | 
            -
                 */
         | 
| 3359 | 
            -
                isSessionSelected() {
         | 
| 3360 | 
            -
                    const sessionSelect = document.getElementById('session-select');
         | 
| 3361 | 
            -
                    return sessionSelect && sessionSelect.value && sessionSelect.value !== '';
         | 
| 3362 | 
            -
                }
         | 
| 3363 | 
            -
             | 
| 3364 | 
            -
                /**
         | 
| 3365 | 
            -
                 * Update HUD display based on current mode
         | 
| 3366 | 
            -
                 */
         | 
| 3367 | 
            -
                updateHUDDisplay() {
         | 
| 3368 | 
            -
                    const eventsWrapper = document.querySelector('.events-wrapper');
         | 
| 3369 | 
            -
                    const hudToggleBtn = document.getElementById('hud-toggle-btn');
         | 
| 3370 | 
            -
                    
         | 
| 3371 | 
            -
                    if (!eventsWrapper || !hudToggleBtn) return;
         | 
| 3372 | 
            -
             | 
| 3373 | 
            -
                    if (this.hudMode) {
         | 
| 3374 | 
            -
                        // Switch to HUD mode
         | 
| 3375 | 
            -
                        eventsWrapper.classList.add('hud-mode');
         | 
| 3376 | 
            -
                        hudToggleBtn.classList.add('btn-hud-active');
         | 
| 3377 | 
            -
                        hudToggleBtn.textContent = 'Normal View';
         | 
| 3378 | 
            -
                        
         | 
| 3379 | 
            -
                        // Activate the HUD visualizer (with lazy loading)
         | 
| 3380 | 
            -
                        if (this.hudVisualizer) {
         | 
| 3381 | 
            -
                            this.hudVisualizer.activate().then(() => {
         | 
| 3382 | 
            -
                                // Process existing events after libraries are loaded
         | 
| 3383 | 
            -
                                this.processExistingEventsForHUD();
         | 
| 3384 | 
            -
                            }).catch((error) => {
         | 
| 3385 | 
            -
                                console.error('Failed to activate HUD:', error);
         | 
| 3386 | 
            -
                                // Optionally revert HUD mode on failure
         | 
| 3387 | 
            -
                                // this.hudMode = false;
         | 
| 3388 | 
            -
                                // this.updateHUDDisplay();
         | 
| 3389 | 
            -
                            });
         | 
| 3390 | 
            -
                        }
         | 
| 3391 | 
            -
                    } else {
         | 
| 3392 | 
            -
                        // Switch to normal mode
         | 
| 3393 | 
            -
                        eventsWrapper.classList.remove('hud-mode');
         | 
| 3394 | 
            -
                        hudToggleBtn.classList.remove('btn-hud-active');
         | 
| 3395 | 
            -
                        hudToggleBtn.textContent = 'HUD';
         | 
| 3396 | 
            -
                        
         | 
| 3397 | 
            -
                        // Deactivate the HUD visualizer
         | 
| 3398 | 
            -
                        if (this.hudVisualizer) {
         | 
| 3399 | 
            -
                            this.hudVisualizer.deactivate();
         | 
| 3400 | 
            -
                        }
         | 
| 3401 | 
            -
                    }
         | 
| 3402 | 
            -
                }
         | 
| 3403 | 
            -
             | 
| 3404 | 
            -
                /**
         | 
| 3405 | 
            -
                 * Update HUD button state based on session selection
         | 
| 3406 | 
            -
                 */
         | 
| 3407 | 
            -
                updateHUDButtonState() {
         | 
| 3408 | 
            -
                    const hudToggleBtn = document.getElementById('hud-toggle-btn');
         | 
| 3409 | 
            -
                    if (!hudToggleBtn) return;
         | 
| 3410 | 
            -
             | 
| 3411 | 
            -
                    if (this.isSessionSelected()) {
         | 
| 3412 | 
            -
                        hudToggleBtn.disabled = false;
         | 
| 3413 | 
            -
                        hudToggleBtn.title = 'Toggle HUD visualizer';
         | 
| 3414 | 
            -
                    } else {
         | 
| 3415 | 
            -
                        hudToggleBtn.disabled = true;
         | 
| 3416 | 
            -
                        hudToggleBtn.title = 'Select a session to enable HUD';
         | 
| 3417 | 
            -
                        
         | 
| 3418 | 
            -
                        // If HUD is currently active, turn it off
         | 
| 3419 | 
            -
                        if (this.hudMode) {
         | 
| 3420 | 
            -
                            this.hudMode = false;
         | 
| 3421 | 
            -
                            this.updateHUDDisplay();
         | 
| 3422 | 
            -
                        }
         | 
| 3423 | 
            -
                    }
         | 
| 3424 | 
            -
                }
         | 
| 3425 | 
            -
             | 
| 3426 | 
            -
                /**
         | 
| 3427 | 
            -
                 * Process existing events for HUD visualization
         | 
| 3428 | 
            -
                 */
         | 
| 3429 | 
            -
                processExistingEventsForHUD() {
         | 
| 3430 | 
            -
                    if (!this.hudVisualizer || !this.eventViewer) return;
         | 
| 3431 | 
            -
                    
         | 
| 3432 | 
            -
                    console.log('๐ Processing existing events for HUD visualization...');
         | 
| 3433 | 
            -
                    
         | 
| 3434 | 
            -
                    // Clear existing visualization
         | 
| 3435 | 
            -
                    this.hudVisualizer.clear();
         | 
| 3436 | 
            -
                    
         | 
| 3437 | 
            -
                    // Get all events (not just filtered ones) to build complete tree structure
         | 
| 3438 | 
            -
                    const allEvents = this.eventViewer.getAllEvents();
         | 
| 3439 | 
            -
                    
         | 
| 3440 | 
            -
                    if (allEvents.length === 0) {
         | 
| 3441 | 
            -
                        console.log('โ No events available for HUD visualization');
         | 
| 3442 | 
            -
                        return;
         | 
| 3443 | 
            -
                    }
         | 
| 3444 | 
            -
                    
         | 
| 3445 | 
            -
                    // Sort events chronologically to ensure proper tree building
         | 
| 3446 | 
            -
                    const sortedEvents = [...allEvents].sort((a, b) => {
         | 
| 3447 | 
            -
                        const timeA = new Date(a.timestamp).getTime();
         | 
| 3448 | 
            -
                        const timeB = new Date(b.timestamp).getTime();
         | 
| 3449 | 
            -
                        return timeA - timeB;
         | 
| 3450 | 
            -
                    });
         | 
| 3451 | 
            -
                    
         | 
| 3452 | 
            -
                    console.log(`๐ Processing ${sortedEvents.length} events chronologically for HUD:`);
         | 
| 3453 | 
            -
                    console.log(`   โข Earliest: ${new Date(sortedEvents[0].timestamp).toLocaleString()}`);
         | 
| 3454 | 
            -
                    console.log(`   โข Latest: ${new Date(sortedEvents[sortedEvents.length - 1].timestamp).toLocaleString()}`);
         | 
| 3455 | 
            -
                    
         | 
| 3456 | 
            -
                    // Process events with enhanced hierarchy building
         | 
| 3457 | 
            -
                    this.hudVisualizer.processExistingEvents(sortedEvents);
         | 
| 3458 | 
            -
                    
         | 
| 3459 | 
            -
                    console.log(`โ
 Processed ${sortedEvents.length} events for HUD visualization`);
         | 
| 3460 | 
            -
                }
         | 
| 3461 | 
            -
             | 
| 3462 | 
            -
                /**
         | 
| 3463 | 
            -
                 * Handle new socket events for HUD
         | 
| 3464 | 
            -
                 * @param {Object} event - Socket event data
         | 
| 3465 | 
            -
                 */
         | 
| 3466 | 
            -
                handleHUDEvent(event) {
         | 
| 3467 | 
            -
                    if (this.hudMode && this.hudVisualizer) {
         | 
| 3468 | 
            -
                        this.hudVisualizer.processEvent(event);
         | 
| 3469 | 
            -
                    }
         | 
| 3470 | 
            -
                }
         | 
| 3471 | 
            -
            }
         | 
| 3472 | 
            -
             | 
| 3473 | 
            -
            // Global functions for backward compatibility
         | 
| 3474 | 
            -
            window.connectSocket = function() {
         | 
| 3475 | 
            -
                if (window.dashboard) {
         | 
| 3476 | 
            -
                    const port = document.getElementById('port-input')?.value || '8765';
         | 
| 3477 | 
            -
                    window.dashboard.socketClient.connect(port);
         | 
| 3478 | 
            -
                }
         | 
| 3479 | 
            -
            };
         | 
| 3480 | 
            -
             | 
| 3481 | 
            -
            window.disconnectSocket = function() {
         | 
| 3482 | 
            -
                if (window.dashboard) {
         | 
| 3483 | 
            -
                    window.dashboard.socketClient.disconnect();
         | 
| 3484 | 
            -
                }
         | 
| 3485 | 
            -
            };
         | 
| 3486 | 
            -
             | 
| 3487 | 
            -
            window.clearEvents = function() {
         | 
| 3488 | 
            -
                if (window.dashboard) {
         | 
| 3489 | 
            -
                    window.dashboard.clearEvents();
         | 
| 3490 | 
            -
                }
         | 
| 3491 | 
            -
            };
         | 
| 3492 | 
            -
             | 
| 3493 | 
            -
            window.exportEvents = function() {
         | 
| 3494 | 
            -
                if (window.dashboard) {
         | 
| 3495 | 
            -
                    window.dashboard.exportEvents();
         | 
| 3496 | 
            -
                }
         | 
| 3497 | 
            -
            };
         | 
| 3498 | 
            -
             | 
| 3499 | 
            -
            window.clearSelection = function() {
         | 
| 3500 | 
            -
                if (window.dashboard) {
         | 
| 3501 | 
            -
                    window.dashboard.clearSelection();
         | 
| 3502 | 
            -
                }
         | 
| 3503 | 
            -
            };
         | 
| 3504 | 
            -
             | 
| 3505 | 
            -
            window.switchTab = function(tabName) {
         | 
| 3506 | 
            -
                if (window.dashboard) {
         | 
| 3507 | 
            -
                    window.dashboard.switchTab(tabName);
         | 
| 3508 | 
            -
                }
         | 
| 3509 | 
            -
            };
         | 
| 3510 | 
            -
             | 
| 3511 | 
            -
            // Detail view functions
         | 
| 3512 | 
            -
            window.showAgentDetailsByIndex = function(index) {
         | 
| 3513 | 
            -
                if (window.dashboard) {
         | 
| 3514 | 
            -
                    window.dashboard.showAgentDetailsByIndex(index);
         | 
| 3515 | 
            -
                }
         | 
| 3516 | 
            -
            };
         | 
| 3517 | 
            -
             | 
| 3518 | 
            -
            window.showToolCallDetails = function(toolCallKey) {
         | 
| 3519 | 
            -
                if (window.dashboard) {
         | 
| 3520 | 
            -
                    window.dashboard.showToolCallDetails(toolCallKey);
         | 
| 3521 | 
            -
                }
         | 
| 3522 | 
            -
            };
         | 
| 3523 | 
            -
             | 
| 3524 | 
            -
            window.showFileDetails = function(filePath) {
         | 
| 3525 | 
            -
                if (window.dashboard) {
         | 
| 3526 | 
            -
                    window.dashboard.showFileDetails(filePath);
         | 
| 3527 | 
            -
                }
         | 
| 3528 | 
            -
            };
         | 
| 3529 | 
            -
             | 
| 3530 | 
            -
            // Debug function for testing scroll functionality
         | 
| 3531 | 
            -
            window.testScroll = async function() {
         | 
| 3532 | 
            -
                if (window.dashboard) {
         | 
| 3533 | 
            -
                    console.log('๐งช Starting scroll functionality test...');
         | 
| 3534 | 
            -
                    try {
         | 
| 3535 | 
            -
                        const result = await window.dashboard.testScrollFunctionality();
         | 
| 3536 | 
            -
                        console.log('๐ Test results:\n' + result);
         | 
| 3537 | 
            -
                        
         | 
| 3538 | 
            -
                        // Also display results in an alert for easier viewing
         | 
| 3539 | 
            -
                        alert('Scroll Test Results:\n\n' + result);
         | 
| 3540 | 
            -
                        
         | 
| 3541 | 
            -
                        return result;
         | 
| 3542 | 
            -
                    } catch (error) {
         | 
| 3543 | 
            -
                        const errorMsg = `โ Test failed with error: ${error.message}`;
         | 
| 3544 | 
            -
                        console.error(errorMsg, error);
         | 
| 3545 | 
            -
                        alert(errorMsg);
         | 
| 3546 | 
            -
                        return errorMsg;
         | 
| 3547 | 
            -
                    }
         | 
| 3548 | 
            -
                } else {
         | 
| 3549 | 
            -
                    const errorMsg = 'โ Dashboard not initialized';
         | 
| 3550 | 
            -
                    console.error(errorMsg);
         | 
| 3551 | 
            -
                    alert(errorMsg);
         | 
| 3552 | 
            -
                    return errorMsg;
         | 
| 3553 | 
            -
                }
         | 
| 3554 | 
            -
            };
         | 
| 3555 | 
            -
             | 
| 3556 | 
            -
            // Simple direct scroll test function
         | 
| 3557 | 
            -
            window.testDirectScroll = function() {
         | 
| 3558 | 
            -
                if (!window.dashboard) {
         | 
| 3559 | 
            -
                    console.error('โ Dashboard not initialized');
         | 
| 3560 | 
            -
                    return 'Dashboard not initialized';
         | 
| 3561 | 
            -
                }
         | 
| 3562 | 
            -
                
         | 
| 3563 | 
            -
                const currentTab = window.dashboard.currentTab;
         | 
| 3564 | 
            -
                const listId = `${currentTab}-list`;
         | 
| 3565 | 
            -
                const element = document.getElementById(listId);
         | 
| 3566 | 
            -
                
         | 
| 3567 | 
            -
                if (!element) {
         | 
| 3568 | 
            -
                    const msg = `โ Element ${listId} not found`;
         | 
| 3569 | 
            -
                    console.error(msg);
         | 
| 3570 | 
            -
                    return msg;
         | 
| 3571 | 
            -
                }
         | 
| 3572 | 
            -
                
         | 
| 3573 | 
            -
                console.log(`๐ฏ Direct scroll test on ${listId}`);
         | 
| 3574 | 
            -
                console.log(`Before: scrollTop=${element.scrollTop}, scrollHeight=${element.scrollHeight}, clientHeight=${element.clientHeight}`);
         | 
| 3575 | 
            -
                
         | 
| 3576 | 
            -
                // Try direct assignment to maximum scroll
         | 
| 3577 | 
            -
                element.scrollTop = 999999;
         | 
| 3578 | 
            -
                
         | 
| 3579 | 
            -
                setTimeout(() => {
         | 
| 3580 | 
            -
                    console.log(`After: scrollTop=${element.scrollTop}, scrollHeight=${element.scrollHeight}, clientHeight=${element.clientHeight}`);
         | 
| 3581 | 
            -
                    const success = element.scrollTop > 0 || element.scrollHeight <= element.clientHeight;
         | 
| 3582 | 
            -
                    const result = `${success ? 'โ
' : 'โ'} Direct scroll test: scrollTop=${element.scrollTop}`;
         | 
| 3583 | 
            -
                    console.log(result);
         | 
| 3584 | 
            -
                    alert(result);
         | 
| 3585 | 
            -
                    return result;
         | 
| 3586 | 
            -
                }, 50);
         | 
| 3587 | 
            -
                
         | 
| 3588 | 
            -
                return 'Test running...';
         | 
| 3589 | 
            -
            };
         | 
| 3590 | 
            -
             | 
| 3591 | 
            -
            // CSS layout diagnostic function
         | 
| 3592 | 
            -
            window.diagnoseCSSLayout = function() {
         | 
| 3593 | 
            -
                if (!window.dashboard) {
         | 
| 3594 | 
            -
                    return 'Dashboard not initialized';
         | 
| 3595 | 
            -
                }
         | 
| 3596 | 
            -
                
         | 
| 3597 | 
            -
                const currentTab = window.dashboard.currentTab;
         | 
| 3598 | 
            -
                const listId = `${currentTab}-list`;
         | 
| 3599 | 
            -
                const results = [];
         | 
| 3600 | 
            -
                
         | 
| 3601 | 
            -
                // Check the full hierarchy
         | 
| 3602 | 
            -
                const containers = [
         | 
| 3603 | 
            -
                    'events-wrapper',
         | 
| 3604 | 
            -
                    'events-container', 
         | 
| 3605 | 
            -
                    `${currentTab}-tab`,
         | 
| 3606 | 
            -
                    listId
         | 
| 3607 | 
            -
                ];
         | 
| 3608 | 
            -
                
         | 
| 3609 | 
            -
                results.push(`๐ CSS Layout Diagnosis for ${currentTab} tab:`);
         | 
| 3610 | 
            -
                results.push('');
         | 
| 3611 | 
            -
                
         | 
| 3612 | 
            -
                containers.forEach(id => {
         | 
| 3613 | 
            -
                    const element = document.getElementById(id);
         | 
| 3614 | 
            -
                    if (element) {
         | 
| 3615 | 
            -
                        const computed = window.getComputedStyle(element);
         | 
| 3616 | 
            -
                        const rect = element.getBoundingClientRect();
         | 
| 3617 | 
            -
                        
         | 
| 3618 | 
            -
                        results.push(`๐ฆ ${id}:`);
         | 
| 3619 | 
            -
                        results.push(`  Display: ${computed.display}`);
         | 
| 3620 | 
            -
                        results.push(`  Position: ${computed.position}`);
         | 
| 3621 | 
            -
                        results.push(`  Width: ${computed.width} (${rect.width}px)`);
         | 
| 3622 | 
            -
                        results.push(`  Height: ${computed.height} (${rect.height}px)`);
         | 
| 3623 | 
            -
                        results.push(`  Max-height: ${computed.maxHeight}`);
         | 
| 3624 | 
            -
                        results.push(`  Overflow-Y: ${computed.overflowY}`);
         | 
| 3625 | 
            -
                        results.push(`  Flex: ${computed.flex}`);
         | 
| 3626 | 
            -
                        results.push(`  Flex-direction: ${computed.flexDirection}`);
         | 
| 3627 | 
            -
                        
         | 
| 3628 | 
            -
                        if (element.scrollHeight !== element.clientHeight) {
         | 
| 3629 | 
            -
                            results.push(`  ๐ ScrollHeight: ${element.scrollHeight}, ClientHeight: ${element.clientHeight}`);
         | 
| 3630 | 
            -
                            results.push(`  ๐ ScrollTop: ${element.scrollTop} (can scroll: ${element.scrollHeight > element.clientHeight})`);
         | 
| 3631 | 
            -
                        }
         | 
| 3632 | 
            -
                        results.push('');
         | 
| 3633 | 
            -
                    } else {
         | 
| 3634 | 
            -
                        results.push(`โ ${id}: Not found`);
         | 
| 3635 | 
            -
                        results.push('');
         | 
| 3636 | 
            -
                    }
         | 
| 3637 | 
            -
                });
         | 
| 3638 | 
            -
                
         | 
| 3639 | 
            -
                const diagnosis = results.join('\n');
         | 
| 3640 | 
            -
                console.log(diagnosis);
         | 
| 3641 | 
            -
                alert(diagnosis);
         | 
| 3642 | 
            -
                return diagnosis;
         | 
| 3643 | 
            -
            };
         | 
| 3644 | 
            -
             | 
| 3645 | 
            -
            // Run all scroll diagnostics
         | 
| 3646 | 
            -
            window.runScrollDiagnostics = async function() {
         | 
| 3647 | 
            -
                console.log('๐ฌ Running complete scroll diagnostics...');
         | 
| 3648 | 
            -
                
         | 
| 3649 | 
            -
                // Step 1: CSS Layout diagnosis
         | 
| 3650 | 
            -
                console.log('\n=== STEP 1: CSS Layout Diagnosis ===');
         | 
| 3651 | 
            -
                const cssResult = window.diagnoseCSSLayout();
         | 
| 3652 | 
            -
                
         | 
| 3653 | 
            -
                // Step 2: Direct scroll test
         | 
| 3654 | 
            -
                console.log('\n=== STEP 2: Direct Scroll Test ===');
         | 
| 3655 | 
            -
                await new Promise(resolve => setTimeout(resolve, 1000));
         | 
| 3656 | 
            -
                const directResult = window.testDirectScroll();
         | 
| 3657 | 
            -
                
         | 
| 3658 | 
            -
                // Step 3: Full scroll functionality test
         | 
| 3659 | 
            -
                console.log('\n=== STEP 3: Full Scroll Functionality Test ===');
         | 
| 3660 | 
            -
                await new Promise(resolve => setTimeout(resolve, 2000));
         | 
| 3661 | 
            -
                const fullResult = await window.testScroll();
         | 
| 3662 | 
            -
                
         | 
| 3663 | 
            -
                const summary = `
         | 
| 3664 | 
            -
            ๐ฌ SCROLL DIAGNOSTICS COMPLETE
         | 
| 3665 | 
            -
            ===============================
         | 
| 3666 | 
            -
             | 
| 3667 | 
            -
            Step 1 - CSS Layout: See console for details
         | 
| 3668 | 
            -
            Step 2 - Direct Scroll: ${directResult}
         | 
| 3669 | 
            -
            Step 3 - Full Test: See alert for details
         | 
| 3670 | 
            -
             | 
| 3671 | 
            -
            Check browser console for complete logs.
         | 
| 3672 | 
            -
                `;
         | 
| 3673 | 
            -
                
         | 
| 3674 | 
            -
                console.log(summary);
         | 
| 3675 | 
            -
                return summary;
         | 
| 3676 | 
            -
            };
         | 
| 3677 | 
            -
             | 
| 3678 | 
            -
            // Git Diff Modal Functions
         | 
| 3679 | 
            -
            window.showGitDiffModal = function(filePath, timestamp, workingDir) {
         | 
| 3680 | 
            -
                // Use the dashboard's current working directory if not provided
         | 
| 3681 | 
            -
                if (!workingDir && window.dashboard && window.dashboard.currentWorkingDir) {
         | 
| 3682 | 
            -
                    workingDir = window.dashboard.currentWorkingDir;
         | 
| 3683 | 
            -
                }
         | 
| 3684 | 
            -
                
         | 
| 3685 | 
            -
                // Create modal if it doesn't exist
         | 
| 3686 | 
            -
                let modal = document.getElementById('git-diff-modal');
         | 
| 3687 | 
            -
                if (!modal) {
         | 
| 3688 | 
            -
                    modal = createGitDiffModal();
         | 
| 3689 | 
            -
                    document.body.appendChild(modal);
         | 
| 3690 | 
            -
                }
         | 
| 3691 | 
            -
                
         | 
| 3692 | 
            -
                // Update modal content
         | 
| 3693 | 
            -
                updateGitDiffModal(modal, filePath, timestamp, workingDir);
         | 
| 3694 | 
            -
                
         | 
| 3695 | 
            -
                // Show the modal as flex container
         | 
| 3696 | 
            -
                modal.style.display = 'flex';
         | 
| 3697 | 
            -
                document.body.style.overflow = 'hidden'; // Prevent background scrolling
         | 
| 3698 | 
            -
            };
         | 
| 3699 | 
            -
             | 
| 3700 | 
            -
            window.hideGitDiffModal = function() {
         | 
| 3701 | 
            -
                const modal = document.getElementById('git-diff-modal');
         | 
| 3702 | 
            -
                if (modal) {
         | 
| 3703 | 
            -
                    modal.style.display = 'none';
         | 
| 3704 | 
            -
                    document.body.style.overflow = ''; // Restore background scrolling
         | 
| 3705 | 
            -
                }
         | 
| 3706 | 
            -
            };
         | 
| 3707 | 
            -
             | 
| 3708 | 
            -
            function createGitDiffModal() {
         | 
| 3709 | 
            -
                const modal = document.createElement('div');
         | 
| 3710 | 
            -
                modal.id = 'git-diff-modal';
         | 
| 3711 | 
            -
                modal.className = 'modal git-diff-modal';
         | 
| 3712 | 
            -
                
         | 
| 3713 | 
            -
                modal.innerHTML = `
         | 
| 3714 | 
            -
                    <div class="modal-content git-diff-content">
         | 
| 3715 | 
            -
                        <div class="git-diff-header">
         | 
| 3716 | 
            -
                            <h2 class="git-diff-title">
         | 
| 3717 | 
            -
                                <span class="git-diff-icon">๐</span>
         | 
| 3718 | 
            -
                                <span class="git-diff-title-text">Git Diff</span>
         | 
| 3719 | 
            -
                            </h2>
         | 
| 3720 | 
            -
                            <div class="git-diff-meta">
         | 
| 3721 | 
            -
                                <span class="git-diff-file-path"></span>
         | 
| 3722 | 
            -
                                <span class="git-diff-timestamp"></span>
         | 
| 3723 | 
            -
                            </div>
         | 
| 3724 | 
            -
                            <button class="git-diff-close" onclick="hideGitDiffModal()">
         | 
| 3725 | 
            -
                                <span>×</span>
         | 
| 3726 | 
            -
                            </button>
         | 
| 3727 | 
            -
                        </div>
         | 
| 3728 | 
            -
                        <div class="git-diff-body">
         | 
| 3729 | 
            -
                            <div class="git-diff-loading">
         | 
| 3730 | 
            -
                                <div class="loading-spinner"></div>
         | 
| 3731 | 
            -
                                <span>Loading git diff...</span>
         | 
| 3732 | 
            -
                            </div>
         | 
| 3733 | 
            -
                            <div class="git-diff-error" style="display: none;">
         | 
| 3734 | 
            -
                                <div class="error-icon">โ ๏ธ</div>
         | 
| 3735 | 
            -
                                <div class="error-message"></div>
         | 
| 3736 | 
            -
                                <div class="error-suggestions"></div>
         | 
| 3737 | 
            -
                            </div>
         | 
| 3738 | 
            -
                            <div class="git-diff-content-area" style="display: none;">
         | 
| 3739 | 
            -
                                <div class="git-diff-toolbar">
         | 
| 3740 | 
            -
                                    <div class="git-diff-info">
         | 
| 3741 | 
            -
                                        <span class="commit-hash"></span>
         | 
| 3742 | 
            -
                                        <span class="diff-method"></span>
         | 
| 3743 | 
            -
                                    </div>
         | 
| 3744 | 
            -
                                    <div class="git-diff-actions">
         | 
| 3745 | 
            -
                                        <button class="git-diff-copy" onclick="copyGitDiff()">
         | 
| 3746 | 
            -
                                            ๐ Copy
         | 
| 3747 | 
            -
                                        </button>
         | 
| 3748 | 
            -
                                    </div>
         | 
| 3749 | 
            -
                                </div>
         | 
| 3750 | 
            -
                                <div class="git-diff-scroll-wrapper">
         | 
| 3751 | 
            -
                                    <pre class="git-diff-display"><code class="git-diff-code"></code></pre>
         | 
| 3752 | 
            -
                                </div>
         | 
| 3753 | 
            -
                            </div>
         | 
| 3754 | 
            -
                        </div>
         | 
| 3755 | 
            -
                    </div>
         | 
| 3756 | 
            -
                `;
         | 
| 3757 | 
            -
                
         | 
| 3758 | 
            -
                // Close modal when clicking outside
         | 
| 3759 | 
            -
                modal.addEventListener('click', (e) => {
         | 
| 3760 | 
            -
                    if (e.target === modal) {
         | 
| 3761 | 
            -
                        hideGitDiffModal();
         | 
| 3762 | 
            -
                    }
         | 
| 3763 | 
            -
                });
         | 
| 3764 | 
            -
                
         | 
| 3765 | 
            -
                // Close modal with Escape key
         | 
| 3766 | 
            -
                document.addEventListener('keydown', (e) => {
         | 
| 3767 | 
            -
                    if (e.key === 'Escape' && modal.style.display === 'block') {
         | 
| 3768 | 
            -
                        hideGitDiffModal();
         | 
| 3769 | 
            -
                    }
         | 
| 3770 | 
            -
                });
         | 
| 3771 | 
            -
                
         | 
| 3772 | 
            -
                return modal;
         | 
| 3773 | 
            -
            }
         | 
| 3774 | 
            -
             | 
| 3775 | 
            -
            async function updateGitDiffModal(modal, filePath, timestamp, workingDir) {
         | 
| 3776 | 
            -
                // Update header info
         | 
| 3777 | 
            -
                const filePathElement = modal.querySelector('.git-diff-file-path');
         | 
| 3778 | 
            -
                const timestampElement = modal.querySelector('.git-diff-timestamp');
         | 
| 3779 | 
            -
                
         | 
| 3780 | 
            -
                filePathElement.textContent = filePath;
         | 
| 3781 | 
            -
                timestampElement.textContent = timestamp ? new Date(timestamp).toLocaleString() : 'Latest';
         | 
| 3782 | 
            -
                
         | 
| 3783 | 
            -
                // Show loading state
         | 
| 3784 | 
            -
                modal.querySelector('.git-diff-loading').style.display = 'flex';
         | 
| 3785 | 
            -
                modal.querySelector('.git-diff-error').style.display = 'none';
         | 
| 3786 | 
            -
                modal.querySelector('.git-diff-content-area').style.display = 'none';
         | 
| 3787 | 
            -
                
         | 
| 3788 | 
            -
                try {
         | 
| 3789 | 
            -
                    // Get the Socket.IO server port with multiple fallbacks
         | 
| 3790 | 
            -
                    let port = 8765; // Default fallback
         | 
| 3791 | 
            -
                    
         | 
| 3792 | 
            -
                    // Try to get port from socketClient first
         | 
| 3793 | 
            -
                    if (window.dashboard && window.dashboard.socketClient && window.dashboard.socketClient.port) {
         | 
| 3794 | 
            -
                        port = window.dashboard.socketClient.port;
         | 
| 3795 | 
            -
                    }
         | 
| 3796 | 
            -
                    // Fallback to port input field if socketClient port is not available
         | 
| 3797 | 
            -
                    else {
         | 
| 3798 | 
            -
                        const portInput = document.getElementById('port-input');
         | 
| 3799 | 
            -
                        if (portInput && portInput.value) {
         | 
| 3800 | 
            -
                            port = portInput.value;
         | 
| 3801 | 
            -
                        }
         | 
| 3802 | 
            -
                    }
         | 
| 3803 | 
            -
                    
         | 
| 3804 | 
            -
                    
         | 
| 3805 | 
            -
                    // Build URL parameters
         | 
| 3806 | 
            -
                    const params = new URLSearchParams({
         | 
| 3807 | 
            -
                        file: filePath
         | 
| 3808 | 
            -
                    });
         | 
| 3809 | 
            -
                    
         | 
| 3810 | 
            -
                    if (timestamp) {
         | 
| 3811 | 
            -
                        params.append('timestamp', timestamp);
         | 
| 3812 | 
            -
                    }
         | 
| 3813 | 
            -
                    if (workingDir) {
         | 
| 3814 | 
            -
                        params.append('working_dir', workingDir);
         | 
| 3815 | 
            -
                    }
         | 
| 3816 | 
            -
                    
         | 
| 3817 | 
            -
                    const requestUrl = `http://localhost:${port}/api/git-diff?${params}`;
         | 
| 3818 | 
            -
                    console.log('๐ Making git diff request to:', requestUrl);
         | 
| 3819 | 
            -
                    
         | 
| 3820 | 
            -
                    // Test server connectivity first
         | 
| 3821 | 
            -
                    try {
         | 
| 3822 | 
            -
                        const healthResponse = await fetch(`http://localhost:${port}/health`, {
         | 
| 3823 | 
            -
                            method: 'GET',
         | 
| 3824 | 
            -
                            headers: {
         | 
| 3825 | 
            -
                                'Accept': 'application/json',
         | 
| 3826 | 
            -
                                'Content-Type': 'application/json'
         | 
| 3827 | 
            -
                            },
         | 
| 3828 | 
            -
                            mode: 'cors'
         | 
| 3829 | 
            -
                        });
         | 
| 3830 | 
            -
                        
         | 
| 3831 | 
            -
                        if (!healthResponse.ok) {
         | 
| 3832 | 
            -
                            throw new Error(`Server health check failed: ${healthResponse.status} ${healthResponse.statusText}`);
         | 
| 3833 | 
            -
                        }
         | 
| 3834 | 
            -
                        
         | 
| 3835 | 
            -
                        console.log('โ
 Server health check passed');
         | 
| 3836 | 
            -
                    } catch (healthError) {
         | 
| 3837 | 
            -
                        throw new Error(`Cannot reach server at localhost:${port}. Health check failed: ${healthError.message}`);
         | 
| 3838 | 
            -
                    }
         | 
| 3839 | 
            -
                    
         | 
| 3840 | 
            -
                    // Make the actual git diff request
         | 
| 3841 | 
            -
                    const response = await fetch(requestUrl, {
         | 
| 3842 | 
            -
                        method: 'GET',
         | 
| 3843 | 
            -
                        headers: {
         | 
| 3844 | 
            -
                            'Accept': 'application/json',
         | 
| 3845 | 
            -
                            'Content-Type': 'application/json'
         | 
| 3846 | 
            -
                        },
         | 
| 3847 | 
            -
                        mode: 'cors'
         | 
| 3848 | 
            -
                    });
         | 
| 3849 | 
            -
                    
         | 
| 3850 | 
            -
                    if (!response.ok) {
         | 
| 3851 | 
            -
                        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
         | 
| 3852 | 
            -
                    }
         | 
| 3853 | 
            -
                    
         | 
| 3854 | 
            -
                    const result = await response.json();
         | 
| 3855 | 
            -
                    console.log('๐ฆ Git diff response:', result);
         | 
| 3856 | 
            -
                    
         | 
| 3857 | 
            -
                    // Hide loading
         | 
| 3858 | 
            -
                    modal.querySelector('.git-diff-loading').style.display = 'none';
         | 
| 3859 | 
            -
                    
         | 
| 3860 | 
            -
                    if (result.success) {
         | 
| 3861 | 
            -
                        console.log('๐ Displaying successful git diff');
         | 
| 3862 | 
            -
                        // Show successful diff
         | 
| 3863 | 
            -
                        displayGitDiff(modal, result);
         | 
| 3864 | 
            -
                    } else {
         | 
| 3865 | 
            -
                        console.log('โ ๏ธ Displaying git diff error:', result);
         | 
| 3866 | 
            -
                        // Show error
         | 
| 3867 | 
            -
                        displayGitDiffError(modal, result);
         | 
| 3868 | 
            -
                    }
         | 
| 3869 | 
            -
                    
         | 
| 3870 | 
            -
                } catch (error) {
         | 
| 3871 | 
            -
                    console.error('โ Failed to fetch git diff:', error);
         | 
| 3872 | 
            -
                    console.error('Error details:', {
         | 
| 3873 | 
            -
                        name: error.name,
         | 
| 3874 | 
            -
                        message: error.message,
         | 
| 3875 | 
            -
                        stack: error.stack,
         | 
| 3876 | 
            -
                        filePath,
         | 
| 3877 | 
            -
                        timestamp,
         | 
| 3878 | 
            -
                        workingDir
         | 
| 3879 | 
            -
                    });
         | 
| 3880 | 
            -
                    
         | 
| 3881 | 
            -
                    modal.querySelector('.git-diff-loading').style.display = 'none';
         | 
| 3882 | 
            -
                    
         | 
| 3883 | 
            -
                    // Create detailed error message based on error type
         | 
| 3884 | 
            -
                    let errorMessage = `Network error: ${error.message}`;
         | 
| 3885 | 
            -
                    let suggestions = [];
         | 
| 3886 | 
            -
                    
         | 
| 3887 | 
            -
                    if (error.message.includes('Failed to fetch')) {
         | 
| 3888 | 
            -
                        errorMessage = 'Failed to connect to the monitoring server';
         | 
| 3889 | 
            -
                        suggestions = [
         | 
| 3890 | 
            -
                            'Check if the monitoring server is running on port 8765',
         | 
| 3891 | 
            -
                            'Verify the port configuration in the dashboard',
         | 
| 3892 | 
            -
                            'Check browser console for CORS or network errors',
         | 
| 3893 | 
            -
                            'Try refreshing the page and reconnecting'
         | 
| 3894 | 
            -
                        ];
         | 
| 3895 | 
            -
                    } else if (error.message.includes('health check failed')) {
         | 
| 3896 | 
            -
                        errorMessage = error.message;
         | 
| 3897 | 
            -
                        suggestions = [
         | 
| 3898 | 
            -
                            'The server may be starting up - try again in a few seconds',
         | 
| 3899 | 
            -
                            'Check if another process is using port 8765',
         | 
| 3900 | 
            -
                            'Restart the claude-mpm monitoring server'
         | 
| 3901 | 
            -
                        ];
         | 
| 3902 | 
            -
                    } else if (error.message.includes('HTTP')) {
         | 
| 3903 | 
            -
                        errorMessage = `Server error: ${error.message}`;
         | 
| 3904 | 
            -
                        suggestions = [
         | 
| 3905 | 
            -
                            'The server encountered an internal error',
         | 
| 3906 | 
            -
                            'Check the server logs for more details',
         | 
| 3907 | 
            -
                            'Try with a different file or working directory'
         | 
| 3908 | 
            -
                        ];
         | 
| 3909 | 
            -
                    }
         | 
| 3910 | 
            -
                    
         | 
| 3911 | 
            -
                    displayGitDiffError(modal, {
         | 
| 3912 | 
            -
                        error: errorMessage,
         | 
| 3913 | 
            -
                        file_path: filePath,
         | 
| 3914 | 
            -
                        working_dir: workingDir,
         | 
| 3915 | 
            -
                        suggestions: suggestions,
         | 
| 3916 | 
            -
                        debug_info: {
         | 
| 3917 | 
            -
                            error_type: error.name,
         | 
| 3918 | 
            -
                            original_message: error.message,
         | 
| 3919 | 
            -
                            port: window.dashboard?.socketClient?.port || document.getElementById('port-input')?.value || '8765',
         | 
| 3920 | 
            -
                            timestamp: new Date().toISOString()
         | 
| 3921 | 
            -
                        }
         | 
| 3922 | 
            -
                    });
         | 
| 3923 | 
            -
                }
         | 
| 3924 | 
            -
            }
         | 
| 3925 | 
            -
             | 
| 3926 | 
            -
            function highlightGitDiff(diffText) {
         | 
| 3927 | 
            -
                /**
         | 
| 3928 | 
            -
                 * Apply basic syntax highlighting to git diff output
         | 
| 3929 | 
            -
                 * WHY: Git diffs have a standard format that can be highlighted for better readability:
         | 
| 3930 | 
            -
                 * - Lines starting with '+' are additions (green)
         | 
| 3931 | 
            -
                 * - Lines starting with '-' are deletions (red)  
         | 
| 3932 | 
            -
                 * - Lines starting with '@@' are context headers (blue)
         | 
| 3933 | 
            -
                 * - File headers and metadata get special formatting
         | 
| 3934 | 
            -
                 */
         | 
| 3935 | 
            -
                return diffText
         | 
| 3936 | 
            -
                    .split('\n')
         | 
| 3937 | 
            -
                    .map(line => {
         | 
| 3938 | 
            -
                        // Escape HTML entities
         | 
| 3939 | 
            -
                        const escaped = line
         | 
| 3940 | 
            -
                            .replace(/&/g, '&')
         | 
| 3941 | 
            -
                            .replace(/</g, '<')
         | 
| 3942 | 
            -
                            .replace(/>/g, '>');
         | 
| 3943 | 
            -
                        
         | 
| 3944 | 
            -
                        // Apply diff highlighting
         | 
| 3945 | 
            -
                        if (line.startsWith('+++') || line.startsWith('---')) {
         | 
| 3946 | 
            -
                            return `<span class="diff-header">${escaped}</span>`;
         | 
| 3947 | 
            -
                        } else if (line.startsWith('@@')) {
         | 
| 3948 | 
            -
                            return `<span class="diff-meta">${escaped}</span>`;
         | 
| 3949 | 
            -
                        } else if (line.startsWith('+')) {
         | 
| 3950 | 
            -
                            return `<span class="diff-addition">${escaped}</span>`;
         | 
| 3951 | 
            -
                        } else if (line.startsWith('-')) {
         | 
| 3952 | 
            -
                            return `<span class="diff-deletion">${escaped}</span>`;
         | 
| 3953 | 
            -
                        } else if (line.startsWith('commit ') || line.startsWith('Author:') || line.startsWith('Date:')) {
         | 
| 3954 | 
            -
                            return `<span class="diff-header">${escaped}</span>`;
         | 
| 3955 | 
            -
                        } else {
         | 
| 3956 | 
            -
                            return `<span class="diff-context">${escaped}</span>`;
         | 
| 3957 | 
            -
                        }
         | 
| 3958 | 
            -
                    })
         | 
| 3959 | 
            -
                    .join('\n');
         | 
| 3960 | 
            -
            }
         | 
| 3961 | 
            -
             | 
| 3962 | 
            -
            function displayGitDiff(modal, result) {
         | 
| 3963 | 
            -
                console.log('๐ displayGitDiff called with:', result);
         | 
| 3964 | 
            -
                const contentArea = modal.querySelector('.git-diff-content-area');
         | 
| 3965 | 
            -
                const commitHashElement = modal.querySelector('.commit-hash');
         | 
| 3966 | 
            -
                const methodElement = modal.querySelector('.diff-method');
         | 
| 3967 | 
            -
                const codeElement = modal.querySelector('.git-diff-code');
         | 
| 3968 | 
            -
                
         | 
| 3969 | 
            -
                console.log('๐ Elements found:', {
         | 
| 3970 | 
            -
                    contentArea: !!contentArea,
         | 
| 3971 | 
            -
                    commitHashElement: !!commitHashElement,
         | 
| 3972 | 
            -
                    methodElement: !!methodElement,
         | 
| 3973 | 
            -
                    codeElement: !!codeElement
         | 
| 3974 | 
            -
                });
         | 
| 3975 | 
            -
                
         | 
| 3976 | 
            -
                // Update metadata
         | 
| 3977 | 
            -
                if (commitHashElement) commitHashElement.textContent = `Commit: ${result.commit_hash}`;
         | 
| 3978 | 
            -
                if (methodElement) methodElement.textContent = `Method: ${result.method}`;
         | 
| 3979 | 
            -
                
         | 
| 3980 | 
            -
                // Update diff content with basic syntax highlighting
         | 
| 3981 | 
            -
                if (codeElement && result.diff) {
         | 
| 3982 | 
            -
                    console.log('๐ก Setting diff content, length:', result.diff.length);
         | 
| 3983 | 
            -
                    codeElement.innerHTML = highlightGitDiff(result.diff);
         | 
| 3984 | 
            -
                    
         | 
| 3985 | 
            -
                    // Force scrolling to work by setting explicit heights
         | 
| 3986 | 
            -
                    const wrapper = modal.querySelector('.git-diff-scroll-wrapper');
         | 
| 3987 | 
            -
                    if (wrapper) {
         | 
| 3988 | 
            -
                        // Give it a moment for content to render
         | 
| 3989 | 
            -
                        setTimeout(() => {
         | 
| 3990 | 
            -
                            const modalContent = modal.querySelector('.modal-content');
         | 
| 3991 | 
            -
                            const header = modal.querySelector('.git-diff-header');
         | 
| 3992 | 
            -
                            const toolbar = modal.querySelector('.git-diff-toolbar');
         | 
| 3993 | 
            -
                            
         | 
| 3994 | 
            -
                            const modalHeight = modalContent?.offsetHeight || 0;
         | 
| 3995 | 
            -
                            const headerHeight = header?.offsetHeight || 0;
         | 
| 3996 | 
            -
                            const toolbarHeight = toolbar?.offsetHeight || 0;
         | 
| 3997 | 
            -
                            
         | 
| 3998 | 
            -
                            const availableHeight = modalHeight - headerHeight - toolbarHeight - 40; // 40px for padding
         | 
| 3999 | 
            -
                            
         | 
| 4000 | 
            -
                            console.log('๐ฏ Setting explicit scroll height:', {
         | 
| 4001 | 
            -
                                modalHeight,
         | 
| 4002 | 
            -
                                headerHeight,
         | 
| 4003 | 
            -
                                toolbarHeight,
         | 
| 4004 | 
            -
                                availableHeight
         | 
| 4005 | 
            -
                            });
         | 
| 4006 | 
            -
                            
         | 
| 4007 | 
            -
                            wrapper.style.maxHeight = `${availableHeight}px`;
         | 
| 4008 | 
            -
                            wrapper.style.overflowY = 'auto';
         | 
| 4009 | 
            -
                        }, 50);
         | 
| 4010 | 
            -
                    }
         | 
| 4011 | 
            -
                } else {
         | 
| 4012 | 
            -
                    console.warn('โ ๏ธ Missing codeElement or diff data');
         | 
| 4013 | 
            -
                }
         | 
| 4014 | 
            -
                
         | 
| 4015 | 
            -
                // Show content area
         | 
| 4016 | 
            -
                if (contentArea) {
         | 
| 4017 | 
            -
                    contentArea.style.display = 'block';
         | 
| 4018 | 
            -
                    console.log('โ
 Content area displayed');
         | 
| 4019 | 
            -
                    
         | 
| 4020 | 
            -
                    // Debug height information and force scrolling test
         | 
| 4021 | 
            -
                    setTimeout(() => {
         | 
| 4022 | 
            -
                        const modal = document.querySelector('.modal-content.git-diff-content');
         | 
| 4023 | 
            -
                        const body = document.querySelector('.git-diff-body');
         | 
| 4024 | 
            -
                        const wrapper = document.querySelector('.git-diff-scroll-wrapper');
         | 
| 4025 | 
            -
                        const display = document.querySelector('.git-diff-display');
         | 
| 4026 | 
            -
                        const code = document.querySelector('.git-diff-code');
         | 
| 4027 | 
            -
                        
         | 
| 4028 | 
            -
                        console.log('๐ Height debugging:', {
         | 
| 4029 | 
            -
                            modalHeight: modal?.offsetHeight,
         | 
| 4030 | 
            -
                            bodyHeight: body?.offsetHeight,
         | 
| 4031 | 
            -
                            wrapperHeight: wrapper?.offsetHeight,
         | 
| 4032 | 
            -
                            wrapperScrollHeight: wrapper?.scrollHeight,
         | 
| 4033 | 
            -
                            displayHeight: display?.offsetHeight,
         | 
| 4034 | 
            -
                            displayScrollHeight: display?.scrollHeight,
         | 
| 4035 | 
            -
                            codeHeight: code?.offsetHeight,
         | 
| 4036 | 
            -
                            wrapperStyle: wrapper ? window.getComputedStyle(wrapper).overflow : null,
         | 
| 4037 | 
            -
                            bodyStyle: body ? window.getComputedStyle(body).overflow : null
         | 
| 4038 | 
            -
                        });
         | 
| 4039 | 
            -
                        
         | 
| 4040 | 
            -
                        // Force test - add a lot of content to test scrolling
         | 
| 4041 | 
            -
                        if (code && code.textContent.length < 1000) {
         | 
| 4042 | 
            -
                            console.log('๐งช Adding test content for scrolling...');
         | 
| 4043 | 
            -
                            code.innerHTML = code.innerHTML + '\n\n' + '// TEST SCROLLING\n'.repeat(100);
         | 
| 4044 | 
            -
                        }
         | 
| 4045 | 
            -
                        
         | 
| 4046 | 
            -
                        // Force fix scrolling with inline styles
         | 
| 4047 | 
            -
                        if (wrapper) {
         | 
| 4048 | 
            -
                            console.log('๐ง Applying scrolling fix...');
         | 
| 4049 | 
            -
                            wrapper.style.height = '100%';
         | 
| 4050 | 
            -
                            wrapper.style.overflow = 'auto';
         | 
| 4051 | 
            -
                            wrapper.style.maxHeight = 'calc(100% - 60px)'; // Account for toolbar
         | 
| 4052 | 
            -
                        }
         | 
| 4053 | 
            -
                        
         | 
| 4054 | 
            -
                        // Also check parent heights
         | 
| 4055 | 
            -
                        const contentArea = document.querySelector('.git-diff-content-area');
         | 
| 4056 | 
            -
                        if (contentArea) {
         | 
| 4057 | 
            -
                            const computedStyle = window.getComputedStyle(contentArea);
         | 
| 4058 | 
            -
                            console.log('๐ Content area height:', computedStyle.height);
         | 
| 4059 | 
            -
                            if (computedStyle.height === 'auto' || !computedStyle.height) {
         | 
| 4060 | 
            -
                                contentArea.style.height = '100%';
         | 
| 4061 | 
            -
                            }
         | 
| 4062 | 
            -
                        }
         | 
| 4063 | 
            -
                    }, 100);
         | 
| 4064 | 
            -
                }
         | 
| 4065 | 
            -
            }
         | 
| 4066 | 
            -
             | 
| 4067 | 
            -
            function displayGitDiffError(modal, result) {
         | 
| 4068 | 
            -
                const errorArea = modal.querySelector('.git-diff-error');
         | 
| 4069 | 
            -
                const messageElement = modal.querySelector('.error-message');
         | 
| 4070 | 
            -
                const suggestionsElement = modal.querySelector('.error-suggestions');
         | 
| 4071 | 
            -
                
         | 
| 4072 | 
            -
                messageElement.textContent = result.error || 'Unknown error occurred';
         | 
| 4073 | 
            -
                
         | 
| 4074 | 
            -
                if (result.suggestions && result.suggestions.length > 0) {
         | 
| 4075 | 
            -
                    suggestionsElement.innerHTML = `
         | 
| 4076 | 
            -
                        <h4>Suggestions:</h4>
         | 
| 4077 | 
            -
                        <ul>
         | 
| 4078 | 
            -
                            ${result.suggestions.map(s => `<li>${s}</li>`).join('')}
         | 
| 4079 | 
            -
                        </ul>
         | 
| 4080 | 
            -
                    `;
         | 
| 4081 | 
            -
                } else {
         | 
| 4082 | 
            -
                    suggestionsElement.innerHTML = '';
         | 
| 4083 | 
            -
                }
         | 
| 4084 | 
            -
                
         | 
| 4085 | 
            -
                errorArea.style.display = 'block';
         | 
| 4086 | 
            -
            }
         | 
| 4087 | 
            -
             | 
| 4088 | 
            -
            window.copyGitDiff = function() {
         | 
| 4089 | 
            -
                const modal = document.getElementById('git-diff-modal');
         | 
| 4090 | 
            -
                if (!modal) return;
         | 
| 4091 | 
            -
                
         | 
| 4092 | 
            -
                const codeElement = modal.querySelector('.git-diff-code');
         | 
| 4093 | 
            -
                if (!codeElement) return;
         | 
| 4094 | 
            -
                
         | 
| 4095 | 
            -
                const text = codeElement.textContent;
         | 
| 4096 | 
            -
                
         | 
| 4097 | 
            -
                if (navigator.clipboard && navigator.clipboard.writeText) {
         | 
| 4098 | 
            -
                    navigator.clipboard.writeText(text).then(() => {
         | 
| 4099 | 
            -
                        // Show brief feedback
         | 
| 4100 | 
            -
                        const button = modal.querySelector('.git-diff-copy');
         | 
| 4101 | 
            -
                        const originalText = button.textContent;
         | 
| 4102 | 
            -
                        button.textContent = 'โ
 Copied!';
         | 
| 4103 | 
            -
                        setTimeout(() => {
         | 
| 4104 | 
            -
                            button.textContent = originalText;
         | 
| 4105 | 
            -
                        }, 2000);
         | 
| 4106 | 
            -
                    }).catch(err => {
         | 
| 4107 | 
            -
                        console.error('Failed to copy text:', err);
         | 
| 4108 | 
            -
                    });
         | 
| 4109 | 
            -
                } else {
         | 
| 4110 | 
            -
                    // Fallback for older browsers
         | 
| 4111 | 
            -
                    const textarea = document.createElement('textarea');
         | 
| 4112 | 
            -
                    textarea.value = text;
         | 
| 4113 | 
            -
                    document.body.appendChild(textarea);
         | 
| 4114 | 
            -
                    textarea.select();
         | 
| 4115 | 
            -
                    document.execCommand('copy');
         | 
| 4116 | 
            -
                    document.body.removeChild(textarea);
         | 
| 4117 | 
            -
                    
         | 
| 4118 | 
            -
                    const button = modal.querySelector('.git-diff-copy');
         | 
| 4119 | 
            -
                    const originalText = button.textContent;
         | 
| 4120 | 
            -
                    button.textContent = 'โ
 Copied!';
         | 
| 4121 | 
            -
                    setTimeout(() => {
         | 
| 4122 | 
            -
                        button.textContent = originalText;
         | 
| 4123 | 
            -
                    }, 2000);
         | 
| 4124 | 
            -
                }
         | 
| 4125 | 
            -
            };
         | 
| 4126 | 
            -
             | 
| 4127 | 
            -
            // Initialize dashboard when DOM is loaded
         | 
| 4128 | 
            -
            document.addEventListener('DOMContentLoaded', () => {
         | 
| 4129 | 
            -
                window.dashboard = new Dashboard();
         | 
| 4130 | 
            -
                console.log('Dashboard ready');
         | 
| 4131 | 
            -
            });
         | 
| 4132 | 
            -
             | 
| 4133 | 
            -
            // Export for use in other modules
         | 
| 4134 | 
            -
            window.Dashboard = Dashboard;
         |