diff --git a/docs/02-usage/060_dashboard.md b/docs/02-usage/060_dashboard.md index 0a7a462a..eeeab1ca 100644 --- a/docs/02-usage/060_dashboard.md +++ b/docs/02-usage/060_dashboard.md @@ -15,11 +15,13 @@ Serena comes with built-in tools for monitoring and managing the current session * the **GUI tool** (disabled by default) The GUI tool is a native application window which displays logs. + It furthermore allows you to shut down the agent and to access the dashboard's URL (if it is running). This is mainly supported on Windows, but it may also work on Linux; macOS is unsupported. Both can be configured in Serena's [configuration](050_configuration) file (`serena_config.yml`). If enabled, they will automatically be opened as soon as the Serena agent/MCP server is started. +For the dashboard, this can be disabled if desired (see below). ## Disabling Automatic Browser Opening @@ -27,5 +29,7 @@ If you prefer not to have the dashboard open automatically (e.g., to avoid focus by setting `web_dashboard_open_on_launch: False` in your `serena_config.yml`. When automatic opening is disabled, you can still access the dashboard by: -* Navigating directly to `http://localhost:24282/dashboard/index.html` in your browser -* Asking the AI to use the `open_dashboard` tool, which will open the dashboard in your default browser +* asking the LLM to "open the Serena dashboard", which will open the dashboard in your default browser + (the tool `open_dashboard` is enabled for this purpose, provided that the dashboard is active, + not opened by default and the GUI tool, which can provide the URL, is not enabled) +* navigating directly to the URL (see above) diff --git a/src/serena/agent.py b/src/serena/agent.py index 7f0c0e9c..a3fe8750 100644 --- a/src/serena/agent.py +++ b/src/serena/agent.py @@ -24,7 +24,7 @@ from serena.ls_manager import LanguageServerManager from serena.project import Project from serena.prompt_factory import SerenaPromptFactory from serena.task_executor import TaskExecutor -from serena.tools import ActivateProjectTool, GetCurrentConfigTool, ReplaceContentTool, Tool, ToolMarker, ToolRegistry +from serena.tools import ActivateProjectTool, GetCurrentConfigTool, OpenDashboardTool, ReplaceContentTool, Tool, ToolMarker, ToolRegistry from serena.util.gui import system_has_usable_display from serena.util.inspection import iter_subclasses from serena.util.logging import MemoryLogHandler @@ -250,8 +250,19 @@ class SerenaAgent: self._check_shell_settings() # determine the base toolset defining the set of exposed tools (which e.g. the MCP shall see), - # limited by the Serena config, the context (which is fixed for the session) and JetBrains mode + # determined by the + # * Serena config, + # * the context (which is fixed for the session) + # * dashboard availability/opening on launch + # * single-project mode reductions (if applicable) + # * JetBrains mode tool_inclusion_definitions: list[ToolInclusionDefinition] = [self.serena_config, self._context] + if ( + self.serena_config.web_dashboard + and not self.serena_config.web_dashboard_open_on_launch + and not self.serena_config.gui_log_window_enabled + ): + tool_inclusion_definitions.append(ToolInclusionDefinition(included_optional_tools=[OpenDashboardTool.get_name_from_cls()])) if self._context.single_project: tool_inclusion_definitions.extend(self._single_project_context_tool_inclusion_definitions(project)) if self.serena_config.language_backend == LanguageBackend.JETBRAINS: @@ -298,14 +309,7 @@ class SerenaAgent: self._dashboard_url = dashboard_url log.info("Serena web dashboard started at %s", dashboard_url) if self.serena_config.web_dashboard_open_on_launch: - if not system_has_usable_display(): - log.warning("Not opening the Serena web dashboard automatically because no usable display was detected.") - else: - # open the dashboard URL in the default web browser (using a separate process to control - # output redirection) - process = multiprocessing.Process(target=self._open_dashboard, args=(dashboard_url,)) - process.start() - process.join(timeout=1) + self.open_dashboard() # inform the GUI window (if any) if self._gui_log_viewer is not None: self._gui_log_viewer.set_dashboard_url(dashboard_url) @@ -415,24 +419,25 @@ class SerenaAgent: """ return self._dashboard_url - def open_dashboard(self) -> str: + def open_dashboard(self) -> bool: """ Opens the Serena web dashboard in the default web browser. :return: a message indicating success or failure """ if self._dashboard_url is None: - return "Error: The web dashboard is not running. Enable it in the Serena configuration." + raise Exception("Dashboard is not running.") if not system_has_usable_display(): - return f"Error: No usable display detected. You can open the dashboard manually at: {self._dashboard_url}" + log.warning("Not opening the Serena web dashboard because no usable display was detected.") + return False # open the dashboard URL in the default web browser (using a separate process to control # output redirection) process = multiprocessing.Process(target=self._open_dashboard, args=(self._dashboard_url,)) process.start() process.join(timeout=1) - return f"Dashboard opened in the default web browser: {self._dashboard_url}" + return True def get_project_root(self) -> str: """ diff --git a/src/serena/tools/config_tools.py b/src/serena/tools/config_tools.py index 265bb2f3..7de51e59 100644 --- a/src/serena/tools/config_tools.py +++ b/src/serena/tools/config_tools.py @@ -2,7 +2,7 @@ from serena.config.context_mode import SerenaAgentMode from serena.tools import Tool, ToolMarkerDoesNotRequireActiveProject, ToolMarkerOptional -class OpenDashboardTool(Tool, ToolMarkerDoesNotRequireActiveProject): +class OpenDashboardTool(Tool, ToolMarkerOptional, ToolMarkerDoesNotRequireActiveProject): """ Opens the Serena web dashboard in the default web browser. The dashboard provides logs, session information, and tool usage statistics. @@ -12,7 +12,10 @@ class OpenDashboardTool(Tool, ToolMarkerDoesNotRequireActiveProject): """ Opens the Serena web dashboard in the default web browser. """ - return self.agent.open_dashboard() + if self.agent.open_dashboard(): + return f"Serena web dashboard has been opened in the user's default web browser: {self.agent.get_dashboard_url()}" + else: + return f"Serena web dashboard could not be opened automatically; tell the user to open it via {self.agent.get_dashboard_url()}" class ActivateProjectTool(Tool, ToolMarkerDoesNotRequireActiveProject):