diff --git a/surfsense_backend/app/agents/multi_agent_chat/main_agent/middleware/dedup_hitl.py b/surfsense_backend/app/agents/multi_agent_chat/main_agent/middleware/dedup_hitl.py index 61af45a22..4ea5f3c2e 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/main_agent/middleware/dedup_hitl.py +++ b/surfsense_backend/app/agents/multi_agent_chat/main_agent/middleware/dedup_hitl.py @@ -29,7 +29,7 @@ from langchain.agents.middleware import AgentMiddleware, AgentState from langchain_core.tools import BaseTool from langgraph.runtime import Runtime -from app.agents.shared.middleware.dedup_tool_calls import ( +from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import ( DedupResolver, wrap_dedup_key_by_arg_name, ) diff --git a/surfsense_backend/app/agents/multi_agent_chat/main_agent/tools/registry.py b/surfsense_backend/app/agents/multi_agent_chat/main_agent/tools/registry.py index f43dfcdfd..515e88eb3 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/main_agent/tools/registry.py +++ b/surfsense_backend/app/agents/multi_agent_chat/main_agent/tools/registry.py @@ -9,7 +9,7 @@ factories for those few tools and nothing else, so the main agent's tool surface stays self-contained and connector-free. Tool *display* metadata for the whole app (the ``/agent/tools`` listing -endpoint) lives separately in :mod:`app.agents.shared.tools.catalog`, a +endpoint) lives separately in :mod:`app.agents.multi_agent_chat.shared.tools.catalog`, a pure-data module that imports no connectors. This registry only governs what the main agent actually builds and binds. """ diff --git a/surfsense_backend/app/agents/shared/middleware/dedup_tool_calls.py b/surfsense_backend/app/agents/multi_agent_chat/shared/middleware/dedup_tool_calls.py similarity index 100% rename from surfsense_backend/app/agents/shared/middleware/dedup_tool_calls.py rename to surfsense_backend/app/agents/multi_agent_chat/shared/middleware/dedup_tool_calls.py diff --git a/surfsense_backend/app/agents/multi_agent_chat/shared/tools/__init__.py b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/__init__.py new file mode 100644 index 000000000..a36be01eb --- /dev/null +++ b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/__init__.py @@ -0,0 +1 @@ +"""Tools shared across multi_agent_chat (main agent + subagents + boundary).""" diff --git a/surfsense_backend/app/agents/shared/tools/catalog.py b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/catalog.py similarity index 100% rename from surfsense_backend/app/agents/shared/tools/catalog.py rename to surfsense_backend/app/agents/multi_agent_chat/shared/tools/catalog.py diff --git a/surfsense_backend/app/agents/shared/tools/hitl.py b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/hitl.py similarity index 98% rename from surfsense_backend/app/agents/shared/tools/hitl.py rename to surfsense_backend/app/agents/multi_agent_chat/shared/tools/hitl.py index dd75dab9c..b779c0a31 100644 --- a/surfsense_backend/app/agents/shared/tools/hitl.py +++ b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/hitl.py @@ -6,7 +6,7 @@ shared by every sensitive tool (native connectors and MCP tools alike). Usage inside a tool:: - from app.agents.shared.tools.hitl import request_approval + from app.agents.multi_agent_chat.shared.tools.hitl import request_approval result = request_approval( action_type="gmail_email_send", diff --git a/surfsense_backend/app/agents/shared/tools/mcp/__init__.py b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/__init__.py similarity index 100% rename from surfsense_backend/app/agents/shared/tools/mcp/__init__.py rename to surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/__init__.py diff --git a/surfsense_backend/app/agents/shared/tools/mcp/cache.py b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/cache.py similarity index 94% rename from surfsense_backend/app/agents/shared/tools/mcp/cache.py rename to surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/cache.py index 8a7365f2c..bb4e40d5b 100644 --- a/surfsense_backend/app/agents/shared/tools/mcp/cache.py +++ b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/cache.py @@ -112,7 +112,9 @@ def refresh_mcp_tools_cache_for_connector( when an event loop is available. Neither path raises. """ try: - from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache + from app.agents.multi_agent_chat.shared.tools.mcp.tool import ( + invalidate_mcp_tools_cache, + ) invalidate_mcp_tools_cache(search_space_id) except Exception: @@ -133,7 +135,9 @@ def refresh_mcp_tools_cache_for_connector( async def _run_connector_prefetch(connector_id: int) -> None: - from app.agents.shared.tools.mcp.tool import discover_single_mcp_connector + from app.agents.multi_agent_chat.shared.tools.mcp.tool import ( + discover_single_mcp_connector, + ) try: await discover_single_mcp_connector(connector_id) diff --git a/surfsense_backend/app/agents/shared/tools/mcp/client.py b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/client.py similarity index 100% rename from surfsense_backend/app/agents/shared/tools/mcp/client.py rename to surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/client.py diff --git a/surfsense_backend/app/agents/shared/tools/mcp/tool.py b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/tool.py similarity index 99% rename from surfsense_backend/app/agents/shared/tools/mcp/tool.py rename to surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/tool.py index 212e94626..7f6bca273 100644 --- a/surfsense_backend/app/agents/shared/tools/mcp/tool.py +++ b/surfsense_backend/app/agents/multi_agent_chat/shared/tools/mcp/tool.py @@ -33,14 +33,16 @@ from sqlalchemy import cast, select from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args -from app.agents.shared.tools.hitl import request_approval -from app.agents.shared.tools.mcp.cache import ( +from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import ( + dedup_key_full_args, +) +from app.agents.multi_agent_chat.shared.tools.hitl import request_approval +from app.agents.multi_agent_chat.shared.tools.mcp.cache import ( CachedMCPTools, read_cached_tools, write_cached_tools, ) -from app.agents.shared.tools.mcp.client import MCPClient +from app.agents.multi_agent_chat.shared.tools.mcp.client import MCPClient from app.db import SearchSourceConnector from app.services.mcp_oauth.registry import MCP_SERVICES, get_service_by_connector_type from app.utils.perf import get_perf_logger diff --git a/surfsense_backend/app/agents/multi_agent_chat/subagents/mcp_tools/index.py b/surfsense_backend/app/agents/multi_agent_chat/subagents/mcp_tools/index.py index a8a2ffcaa..bc3329727 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/subagents/mcp_tools/index.py +++ b/surfsense_backend/app/agents/multi_agent_chat/subagents/mcp_tools/index.py @@ -21,7 +21,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.agents.multi_agent_chat.constants import ( CONNECTOR_TYPE_TO_CONNECTOR_AGENT_MAPS, ) -from app.agents.shared.tools.mcp.tool import load_mcp_tools +from app.agents.multi_agent_chat.shared.tools.mcp.tool import load_mcp_tools from app.db import SearchSourceConnector logger = logging.getLogger(__name__) diff --git a/surfsense_backend/app/agents/shared/tools/__init__.py b/surfsense_backend/app/agents/shared/tools/__init__.py index 21552ad98..342fe9169 100644 --- a/surfsense_backend/app/agents/shared/tools/__init__.py +++ b/surfsense_backend/app/agents/shared/tools/__init__.py @@ -1,14 +1,5 @@ -"""Cross-agent shared tools and tool metadata. +"""Cross-agent shared tools. -Tool *implementations* live with the agents that own them (e.g. deliverable -generators and their knowledge-base search helper under -``subagents/builtins/deliverables/tools``). This package holds only the -genuinely shared piece: the display-metadata catalog. +Only genuinely cross-agent tool code lives here (currently web_search, imported +directly from its module). """ - -from .catalog import TOOL_CATALOG, ToolMetadata - -__all__ = [ - "TOOL_CATALOG", - "ToolMetadata", -] diff --git a/surfsense_backend/app/routes/mcp_oauth_route.py b/surfsense_backend/app/routes/mcp_oauth_route.py index 5a42d86bd..4b7132064 100644 --- a/surfsense_backend/app/routes/mcp_oauth_route.py +++ b/surfsense_backend/app/routes/mcp_oauth_route.py @@ -665,7 +665,7 @@ def _refresh_mcp_cache(connector_id: int, space_id: int) -> None: isolated from the OAuth response flow. """ try: - from app.agents.shared.tools.mcp.cache import ( + from app.agents.multi_agent_chat.shared.tools.mcp.cache import ( refresh_mcp_tools_cache_for_connector, ) diff --git a/surfsense_backend/app/routes/new_chat_routes.py b/surfsense_backend/app/routes/new_chat_routes.py index 0e8ede3ba..0d8c8d461 100644 --- a/surfsense_backend/app/routes/new_chat_routes.py +++ b/surfsense_backend/app/routes/new_chat_routes.py @@ -1668,7 +1668,7 @@ async def list_agent_tools( Hidden (WIP) tools are excluded from the response. """ - from app.agents.shared.tools.catalog import TOOL_CATALOG + from app.agents.multi_agent_chat.shared.tools.catalog import TOOL_CATALOG return [ AgentToolInfo( diff --git a/surfsense_backend/app/routes/search_source_connectors_routes.py b/surfsense_backend/app/routes/search_source_connectors_routes.py index 04407edf3..1c39eee06 100644 --- a/surfsense_backend/app/routes/search_source_connectors_routes.py +++ b/surfsense_backend/app/routes/search_source_connectors_routes.py @@ -43,6 +43,7 @@ from app.db import ( async_session_maker, get_async_session, ) +from app.notifications.service import NotificationService from app.observability import metrics as ot_metrics, otel as ot from app.schemas import ( GoogleDriveIndexRequest, @@ -55,7 +56,6 @@ from app.schemas import ( SearchSourceConnectorUpdate, ) from app.services.composio_service import ComposioService, get_composio_service -from app.notifications.service import NotificationService from app.users import current_active_user # NOTE: connector indexer functions are imported lazily inside each @@ -675,7 +675,9 @@ async def delete_search_source_connector( await session.commit() if is_mcp: - from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache + from app.agents.multi_agent_chat.shared.tools.mcp.tool import ( + invalidate_mcp_tools_cache, + ) invalidate_mcp_tools_cache(search_space_id) @@ -2687,7 +2689,7 @@ async def create_mcp_connector( f"for user {user.id} in search space {search_space_id}" ) - from app.agents.shared.tools.mcp.cache import ( + from app.agents.multi_agent_chat.shared.tools.mcp.cache import ( refresh_mcp_tools_cache_for_connector, ) @@ -2867,7 +2869,7 @@ async def update_mcp_connector( logger.info(f"Updated MCP connector {connector_id}") - from app.agents.shared.tools.mcp.cache import ( + from app.agents.multi_agent_chat.shared.tools.mcp.cache import ( refresh_mcp_tools_cache_for_connector, ) @@ -2927,7 +2929,9 @@ async def delete_mcp_connector( await session.delete(connector) await session.commit() - from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache + from app.agents.multi_agent_chat.shared.tools.mcp.tool import ( + invalidate_mcp_tools_cache, + ) invalidate_mcp_tools_cache(search_space_id) @@ -2966,7 +2970,7 @@ async def test_mcp_server_connection( Connection status and list of available tools """ try: - from app.agents.shared.tools.mcp.client import ( + from app.agents.multi_agent_chat.shared.tools.mcp.client import ( test_mcp_connection, test_mcp_http_connection, ) @@ -3157,7 +3161,9 @@ async def trust_mcp_tool( connectors (``LINEAR_CONNECTOR``, ``JIRA_CONNECTOR``, ...) — the storage primitive is the same JSON list under ``config.trusted_tools``. """ - from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache + from app.agents.multi_agent_chat.shared.tools.mcp.tool import ( + invalidate_mcp_tools_cache, + ) from app.services.user_tool_allowlist import add_user_trust try: @@ -3197,7 +3203,9 @@ async def untrust_mcp_tool( The tool will require HITL approval again on subsequent calls. """ - from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache + from app.agents.multi_agent_chat.shared.tools.mcp.tool import ( + invalidate_mcp_tools_cache, + ) from app.services.user_tool_allowlist import remove_user_trust try: diff --git a/surfsense_backend/tests/e2e/fakes/mcp_runtime.py b/surfsense_backend/tests/e2e/fakes/mcp_runtime.py index afaf7685e..77328735c 100644 --- a/surfsense_backend/tests/e2e/fakes/mcp_runtime.py +++ b/surfsense_backend/tests/e2e/fakes/mcp_runtime.py @@ -137,10 +137,10 @@ def install(active_patches: list[Any]) -> None: """Patch production MCP streamable-HTTP boundaries exactly once.""" targets = [ ( - "app.agents.shared.tools.mcp.tool.streamablehttp_client", + "app.agents.multi_agent_chat.shared.tools.mcp.tool.streamablehttp_client", _fake_streamablehttp_client, ), - ("app.agents.shared.tools.mcp.tool.ClientSession", _FakeClientSession), + ("app.agents.multi_agent_chat.shared.tools.mcp.tool.ClientSession", _FakeClientSession), ] for target, replacement in targets: p = patch(target, replacement) diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_dedup_tool_calls.py b/surfsense_backend/tests/unit/agents/new_chat/test_dedup_tool_calls.py index c64ebc630..52485449c 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_dedup_tool_calls.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_dedup_tool_calls.py @@ -115,7 +115,9 @@ def test_full_args_dedup_keeps_distinct_calls_sharing_a_field() -> None: With :func:`dedup_key_full_args` only fully identical arg dicts dedup. """ - from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args + from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import ( + dedup_key_full_args, + ) tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args) mw = DedupHITLToolCallsMiddleware(agent_tools=[tool]) @@ -157,7 +159,9 @@ def test_full_args_dedup_keeps_distinct_calls_sharing_a_field() -> None: def test_full_args_dedup_drops_only_exact_duplicates() -> None: - from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args + from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import ( + dedup_key_full_args, + ) tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args) mw = DedupHITLToolCallsMiddleware(agent_tools=[tool]) diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_default_permissions_layering.py b/surfsense_backend/tests/unit/agents/new_chat/test_default_permissions_layering.py index f1f02d92d..8f5face70 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_default_permissions_layering.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_default_permissions_layering.py @@ -17,7 +17,7 @@ caused two production-painful behaviors: read-only tool calls, raising ``RejectedError("ls")``. * Mutating connector tools got *double* prompted — once via the middleware ``ask`` and again via the per-tool ``interrupt()`` in - ``app.agents.shared.tools.hitl``. + ``app.agents.multi_agent_chat.shared.tools.hitl``. These tests pin the layering so a refactor that drops the default ruleset fails loud. diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_hitl_auto_approve.py b/surfsense_backend/tests/unit/agents/new_chat/test_hitl_auto_approve.py index 6552d6bc6..5f7ee63f8 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_hitl_auto_approve.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_hitl_auto_approve.py @@ -10,7 +10,7 @@ from __future__ import annotations import pytest -from app.agents.shared.tools.hitl import ( +from app.agents.multi_agent_chat.shared.tools.hitl import ( DEFAULT_AUTO_APPROVED_TOOLS, HITLResult, request_approval, diff --git a/surfsense_backend/tests/unit/agents/new_chat/tools/test_mcp_tools_cache.py b/surfsense_backend/tests/unit/agents/new_chat/tools/test_mcp_tools_cache.py index 6958ef795..2ec08056a 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/tools/test_mcp_tools_cache.py +++ b/surfsense_backend/tests/unit/agents/new_chat/tools/test_mcp_tools_cache.py @@ -7,7 +7,7 @@ from types import SimpleNamespace import pytest -from app.agents.shared.tools.mcp.cache import ( +from app.agents.multi_agent_chat.shared.tools.mcp.cache import ( CachedMCPToolDef, CachedMCPTools, read_cached_tools, diff --git a/surfsense_backend/tests/unit/middleware/test_dedup_hitl_tool_calls.py b/surfsense_backend/tests/unit/middleware/test_dedup_hitl_tool_calls.py index 4646a9590..08ec57707 100644 --- a/surfsense_backend/tests/unit/middleware/test_dedup_hitl_tool_calls.py +++ b/surfsense_backend/tests/unit/middleware/test_dedup_hitl_tool_calls.py @@ -5,7 +5,7 @@ from langchain_core.tools import StructuredTool from app.agents.multi_agent_chat.main_agent.middleware.dedup_hitl import ( DedupHITLToolCallsMiddleware, ) -from app.agents.shared.middleware.dedup_tool_calls import ( +from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import ( wrap_dedup_key_by_arg_name, )