Fix: JetBrains mode prompt was not provided to agents

The mode is now treated as a (background) base mode
in `ActiveModes` which reduces the surface for issues
pertaining to custom handling of modes.
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-06-10 15:57:13 +02:00
1 parent 1d020b9606
commit dc9ea7a1c8
3 files changed
+42 -28

No files matched your search

+2
View File
@@ -14,6 +14,8 @@ Status of the `main` branch. Changes prior to the next official version change w
incorrect file access if a corresponding local file existed (e.g. `./antigravity` binary);
file access is now guarded with path detection (file ending or path separator must be present)
- Allow `query_project` tool to access read-only tools that are not enabled in the current configuration
- Fix: JetBrains mode prompt was not provided to agents; The mode is now treated as a (background) base mode
in `ActiveModes` which reduces the surface for issues pertaining to custom handling of modes.
* Language Servers:
- `typescript_vts`: Add `initialization_options` setting in `ls_specific_settings.typescript_vts`.
+39 -27
View File
@@ -210,7 +210,12 @@ class ToolSet:
class ActiveModes:
_mode_instances: dict[str, SerenaAgentMode] = {}
def __init__(self) -> None:
def __init__(self, background_base_modes: Sequence[SerenaAgentMode] | None = None) -> None:
"""
:param background_base_modes: base modes that are always active in the background and will not be removed
by applications of mode selection definitions via method `apply`
"""
self._background_base_modes = background_base_modes or []
self._configured_base_modes: Sequence[str] | None = None
self._configured_default_modes: Sequence[str] | None = None
self._added_modes: set[str] = set()
@@ -220,7 +225,7 @@ class ActiveModes:
"""
self._active_mode_names: Sequence[str] = []
"""
the full list of active mode names
the full list of active mode names (not including background base modes)
"""
def apply(self, mode_selection: ModeSelectionDefinition) -> None:
@@ -245,6 +250,9 @@ class ActiveModes:
self._active_mode_names = sorted(set(self._configured_base_modes or []) | self._dynamically_activated_mode_names)
def get_mode_names(self) -> Sequence[str]:
"""
:return: the ordered list of active mode names (not including the non-user-facing background base modes)
"""
return self._active_mode_names
@classmethod
@@ -253,14 +261,22 @@ class ActiveModes:
cls._mode_instances[mode_name] = SerenaAgentMode.load(mode_name)
return cls._mode_instances[mode_name]
def get_modes(self) -> Sequence[SerenaAgentMode]:
return [self.get_mode_instance(mode_name) for mode_name in self._active_mode_names]
def get_modes(self, include_background_base_modes: bool = False) -> Sequence[SerenaAgentMode]:
result: list[SerenaAgentMode] = []
if include_background_base_modes:
result.extend(self._background_base_modes)
result.extend([self.get_mode_instance(mode_name) for mode_name in self._active_mode_names])
return result
def get_dynamically_activated_modes(self) -> Sequence[SerenaAgentMode]:
return [self.get_mode_instance(mode_name) for mode_name in self._dynamically_activated_mode_names]
def get_base_modes(self) -> Sequence[SerenaAgentMode]:
return [self.get_mode_instance(mode_name) for mode_name in self._configured_base_modes or []]
def get_base_modes(self, include_background_base_modes: bool = False) -> Sequence[SerenaAgentMode]:
result: list[SerenaAgentMode] = []
if include_background_base_modes:
result.extend(self._background_base_modes)
result.extend([self.get_mode_instance(mode_name) for mode_name in self._configured_base_modes or []])
return result
class ProjectPromptProvisionStatus:
@@ -555,10 +571,6 @@ class SerenaAgent:
# propagate configuration to other components
self.serena_config.propagate_settings()
# initialise active modes (baseline modes prior to project activation)
self._active_modes: ActiveModes
self._update_active_modes(log_message=False)
# determine registered project to be activated (if any)
registered_project_to_activate: RegisteredProject | None = (
self.serena_config.get_registered_project(project, autoregister=True) if project is not None else None
@@ -642,6 +654,10 @@ class SerenaAgent:
# Initialize the prompt factory
self.prompt_factory = SerenaPromptFactory()
# initialise active modes (baseline modes prior to project activation, allowing newly activated modes to be tracked)
self._active_modes: ActiveModes
self._update_active_modes(log_message=False)
# activate the given project (if any), also updating the active modes
# Note: We cannot update the active tools yet, because the base toolset has not been computed yet
# (and its computation depends on the active project)
@@ -653,9 +669,7 @@ class SerenaAgent:
self._update_active_modes()
# determine the base toolset defining the set of exposed tools (which e.g. the MCP shall see),
self._base_toolset = self._create_base_toolset(
self.serena_config, self._language_backend, self._context, self._active_modes, self._active_project
)
self._base_toolset = self._create_base_toolset(self.serena_config, self._context, self._active_modes, self._active_project)
self._exposed_tools = self._base_toolset.to_available_tools(self._all_tools)
log.info(f"Number of exposed tools: {len(self._exposed_tools)}. Exposed tools: {self._exposed_tools.tool_names}")
@@ -719,7 +733,6 @@ class SerenaAgent:
def _create_base_toolset(
cls,
serena_config: SerenaConfig,
language_backend: LanguageBackend,
context: SerenaAgentContext,
modes: ActiveModes,
project: Project | None,
@@ -730,9 +743,9 @@ class SerenaAgent:
* dashboard availability/opening on launch
* Serena config
* the context (which is fixed for the session)
* the optional tools enabled by initial modes
* the base modes (including background base modes like JetBrains mode)
* the optional tools enabled by initial dynamic modes
* single-project mode reductions (if applicable)
* JetBrains mode
"""
# determine whether to include the OpenDashboardTool based on the Serena configuration
tool_inclusion_definitions: list[ToolInclusionDefinition] = []
@@ -751,7 +764,7 @@ class SerenaAgent:
# consider modes
# * base modes: These cannot be changed, so they are fully applied
for base_mode in modes.get_base_modes():
for base_mode in modes.get_base_modes(include_background_base_modes=True):
tool_inclusion_definitions.append(base_mode)
# * dynamically activated modes:
# - When not in a single-project context, these modes can later be turned off,
@@ -788,10 +801,6 @@ class SerenaAgent:
)
tool_inclusion_definitions.append(project.project_config)
# enabled the internal 'jetbrains' mode for the JetBrains backend
if language_backend == LanguageBackend.JETBRAINS:
tool_inclusion_definitions.append(SerenaAgentMode.from_name_internal("jetbrains"))
# compute the resulting tool set
base_toolset = ToolSet.default().apply(*tool_inclusion_definitions)
log.info(f"Number of exposed tools: {len(base_toolset)}")
@@ -912,11 +921,11 @@ class SerenaAgent:
raise ValueError("No active project. Please activate a project first.")
return project
def get_active_modes(self) -> list[SerenaAgentMode]:
def get_active_modes(self) -> ActiveModes:
"""
:return: the list of active modes
:return: the active modes
"""
return list(self._active_modes.get_modes())
return self._active_modes
def _format_prompt(self, prompt_template: str) -> str:
template = JinjaTemplate(prompt_template)
@@ -948,7 +957,7 @@ class SerenaAgent:
# determine modes for which prompts must (still) be provided, excluding modes that were already provided in a
# previously provided project activation message (if any)
relevant_modes = []
for mode in self.get_active_modes():
for mode in self.get_active_modes().get_modes(include_background_base_modes=True):
if mode.has_prompt():
if not self._project_prompt_status.is_mode_prompt_already_provided(mode.name, session_id):
relevant_modes.append(mode)
@@ -1027,7 +1036,10 @@ class SerenaAgent:
Updates the active modes based on the Serena configuration, the active project configuration (if any),
and mode overrides (if any).
"""
self._active_modes = ActiveModes()
background_base_modes = []
if self._language_backend.is_jetbrains():
background_base_modes.append(SerenaAgentMode.from_name_internal("jetbrains"))
self._active_modes = ActiveModes(background_base_modes=background_base_modes)
self._active_modes.apply(self.serena_config)
if self._active_project:
self._active_modes.apply(self._active_project.project_config)
@@ -1260,7 +1272,7 @@ class SerenaAgent:
result_str += f"Active context: {self._context.name}\n"
# Active modes
active_mode_names = [mode.name for mode in self.get_active_modes()]
active_mode_names = self.get_active_modes().get_mode_names()
result_str += "Active modes: {}\n".format(", ".join(active_mode_names)) + "\n"
# Available but not active modes
+1 -1
View File
@@ -502,7 +502,7 @@ class SerenaDashboardAPI:
}
# Get active modes
modes = self._agent.get_active_modes()
modes = self._agent.get_active_modes().get_modes(include_background_base_modes=False)
modes_info = [
{"name": mode.name, "description": mode.description, "path": SerenaAgentMode.get_path(mode.name, instance=mode)}
for mode in modes