diff --git a/nanobot/agent/tools/filesystem.py b/nanobot/agent/tools/filesystem.py index 2c7d8955..2d3eedc2 100644 --- a/nanobot/agent/tools/filesystem.py +++ b/nanobot/agent/tools/filesystem.py @@ -237,6 +237,7 @@ class ReadFileTool(_FsTool): _scopes = {"core", "subagent", "memory"} _MAX_CHARS = 128_000 + _MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024 _DEFAULT_LIMIT = 2000 _MAX_PDF_PAGES = 20 @@ -290,6 +291,15 @@ class ReadFileTool(_FsTool): if not fp.is_file(): return ToolResult.error(f"Error: Not a file: {path}") + file_size = fp.stat().st_size + if file_size > self._MAX_FILE_SIZE_BYTES: + size_mib = file_size / (1024 * 1024) + max_mib = self._MAX_FILE_SIZE_BYTES // (1024 * 1024) + return ToolResult.error( + f"Error: File too large to read ({size_mib:.1f} MiB). " + f"Maximum is {max_mib} MiB." + ) + # PDF support if fp.suffix.lower() == ".pdf": return self._read_pdf(fp, pages) diff --git a/tests/tools/test_filesystem_tools.py b/tests/tools/test_filesystem_tools.py index 52667030..9b917dc6 100644 --- a/tests/tools/test_filesystem_tools.py +++ b/tests/tools/test_filesystem_tools.py @@ -99,6 +99,22 @@ class TestReadFileTool: assert len(result) <= ReadFileTool._MAX_CHARS + 500 # small margin for footer assert "Use offset=" in result + @pytest.mark.asyncio + async def test_oversized_file_is_rejected_before_read(self, tool, tmp_path, monkeypatch): + f = tmp_path / "huge.txt" + with f.open("wb") as stream: + stream.truncate(ReadFileTool._MAX_FILE_SIZE_BYTES + 1) + + def fail_read_bytes(self): + raise AssertionError("oversized file content should not be loaded") + + monkeypatch.setattr(type(f), "read_bytes", fail_read_bytes) + + result = await tool.execute(path=str(f)) + + assert "File too large to read" in result + assert "Maximum is 100 MiB" in result + # --------------------------------------------------------------------------- # _find_match (unit tests for the helper)