Introduce optional facades; make facade availability derived

Facade availability:
* facades can be optional (`Facade.from_api(..., is_optional=True)`), mirroring optional tools
* `Facade.is_enabled()` is derived: a facade is available iff it has at least one enabled method
* `ApiScope.is_facade_enabled` is thereby obsolete and removed

Opt-in rule (uniform for optional facades, excluded facades and optional methods):
* methods of a facade which is not included and optional methods require explicit inclusion
* all other methods are enabled unless explicitly excluded

Application:
* the `ext` facade is optional; the query-projects mode includes it (`included_apis: [ext]`)
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-09-17 10:58:14 +02:00
1 parent 680a7e8fc1
commit 4b54fa737f
7 files changed
+59 -27

No files matched your search

+3
View File
@@ -86,6 +86,9 @@ def test_external_project_context_in_repl(
server, port = project_server
monkeypatch.setattr(ProjectServer, "PORT", port) # let the REPL's external project context use the test server
# enable the optional "ext" facade
serena_config.included_apis = ["ext"]
# the querying agent has another project active and queries the python test project
agent = SerenaAgent(project="test_repo_typescript", serena_config=serena_config)
agent.execute_task(lambda: None)
+16
View File
@@ -217,6 +217,22 @@ class TestFacade:
scope.process(ApiInclusionDefinition(**kwargs))
return scope
def test_optional_facade_is_opt_in(self) -> None:
# an optional facade is disabled unless it is included explicitly
facade = Facade.from_api(self.DummyApi(MagicMock()), ApiScope(), is_optional=True)
assert not facade.is_enabled()
assert facade.enabled_method_names == []
# including the facade enables its non-optional methods
facade = Facade.from_api(self.DummyApi(MagicMock()), self._scope(included_apis=["dummy"]), is_optional=True)
assert facade.is_enabled()
assert set(facade.enabled_method_names) == {"add", "secret", "rarely"}
# including a single method enables the facade with just that method
facade = Facade.from_api(self.DummyApi(MagicMock()), self._scope(included_apis=["dummy.add"]), is_optional=True)
assert facade.is_enabled()
assert facade.enabled_method_names == ["add"]
def test_api_scope_facade_exclusion_and_method_inclusion(self) -> None:
# excluding the facade disables everything
facade = Facade.from_api(self.DummyApi(MagicMock()), self._scope(excluded_apis=["dummy"]))