diff --git a/nanobot/agent/tools/filesystem.py b/nanobot/agent/tools/filesystem.py index 6e439495..9f2f0e4c 100644 --- a/nanobot/agent/tools/filesystem.py +++ b/nanobot/agent/tools/filesystem.py @@ -10,6 +10,7 @@ from typing import Any from nanobot.agent.tools.base import Tool, tool_parameters from nanobot.agent.tools.file_state import FileStates, _hash_file, current_file_states from nanobot.agent.tools.path_utils import resolve_workspace_path +from nanobot.config.schema import Base from nanobot.security.workspace_access import current_tool_workspace from nanobot.agent.tools.schema import ( BooleanSchema, @@ -20,9 +21,25 @@ from nanobot.agent.tools.schema import ( from nanobot.utils.helpers import build_image_content_blocks, detect_image_mime +class FileToolsConfig(Base): + """Filesystem tools configuration.""" + + enable: bool = True # built-in file tools on by default; set false to act only through MCP servers + + class _FsTool(Tool): """Shared base for filesystem tools — common init and path resolution.""" + config_key = "file" + + @classmethod + def config_cls(cls): + return FileToolsConfig + + @classmethod + def enabled(cls, ctx: Any) -> bool: + return ctx.config.file.enable + def __init__( self, workspace: Path | None = None, diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index e57220f7..bc0b1349 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -12,6 +12,7 @@ from nanobot.cron.types import CronSchedule if TYPE_CHECKING: from nanobot.agent.tools.cli_apps import CliAppsToolConfig + from nanobot.agent.tools.filesystem import FileToolsConfig from nanobot.agent.tools.image_generation import ImageGenerationToolConfig from nanobot.agent.tools.self import MyToolConfig from nanobot.agent.tools.shell import ExecToolConfig @@ -320,6 +321,7 @@ class ToolsConfig(Base): web: WebToolsConfig = Field(default_factory=lambda: _lazy_default("nanobot.agent.tools.web", "WebToolsConfig")) exec: ExecToolConfig = Field(default_factory=lambda: _lazy_default("nanobot.agent.tools.shell", "ExecToolConfig")) + file: FileToolsConfig = Field(default_factory=lambda: _lazy_default("nanobot.agent.tools.filesystem", "FileToolsConfig")) cli_apps: CliAppsToolConfig = Field(default_factory=lambda: _lazy_default("nanobot.agent.tools.cli_apps", "CliAppsToolConfig")) my: MyToolConfig = Field(default_factory=lambda: _lazy_default("nanobot.agent.tools.self", "MyToolConfig")) image_generation: ImageGenerationToolConfig = Field( @@ -558,6 +560,7 @@ def _resolve_tool_config_refs() -> None: import sys from nanobot.agent.tools.cli_apps import CliAppsToolConfig + from nanobot.agent.tools.filesystem import FileToolsConfig from nanobot.agent.tools.image_generation import ImageGenerationToolConfig from nanobot.agent.tools.self import MyToolConfig from nanobot.agent.tools.shell import ExecToolConfig @@ -566,6 +569,7 @@ def _resolve_tool_config_refs() -> None: # Re-export into this module's namespace mod = sys.modules[__name__] mod.ExecToolConfig = ExecToolConfig # type: ignore[attr-defined] + mod.FileToolsConfig = FileToolsConfig # type: ignore[attr-defined] mod.CliAppsToolConfig = CliAppsToolConfig # type: ignore[attr-defined] mod.WebToolsConfig = WebToolsConfig # type: ignore[attr-defined] mod.WebSearchConfig = WebSearchConfig # type: ignore[attr-defined] diff --git a/tests/test_file_tool_toggle.py b/tests/test_file_tool_toggle.py new file mode 100644 index 00000000..a3d2ed5e --- /dev/null +++ b/tests/test_file_tool_toggle.py @@ -0,0 +1,16 @@ +from types import SimpleNamespace + +from nanobot.config.schema import Config, ToolsConfig +from nanobot.agent.tools.filesystem import FileToolsConfig, ReadFileTool + + +def test_file_tools_enabled_by_default(): + assert FileToolsConfig().enable is True + assert Config().tools.file.enable is True + + +def test_file_tool_gate_follows_flag(): + cfg = ToolsConfig() + cfg.file.enable = False + assert ReadFileTool.enabled(SimpleNamespace(config=cfg)) is False + assert ReadFileTool.enabled(SimpleNamespace(config=ToolsConfig())) is True