diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/deliverables/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/deliverables/agent.py index e7eeec4db..775624a6f 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/deliverables/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/deliverables/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles deliverables tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/memory/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/memory/agent.py index 2d231e383..f9ee96938 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/memory/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/memory/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles memory tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/research/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/research/agent.py index c04330607..8211686e4 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/research/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/builtins/research/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles research tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/airtable/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/airtable/agent.py index 215e995df..583df0078 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/airtable/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/airtable/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles airtable tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/calendar/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/calendar/agent.py index 89117fd6e..378f2095a 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/calendar/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/calendar/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles calendar tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/clickup/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/clickup/agent.py index fd404cdb0..a4a193332 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/clickup/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/clickup/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles clickup tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/confluence/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/confluence/agent.py index 3b27d39e8..11c4fc0e8 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/confluence/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/confluence/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles confluence tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/discord/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/discord/agent.py index 793de429f..9e1eb964a 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/discord/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/discord/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles discord tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/dropbox/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/dropbox/agent.py index dc26c181b..60a01c20a 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/dropbox/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/dropbox/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles dropbox tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/gmail/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/gmail/agent.py index de4971e1c..6910030c4 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/gmail/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/gmail/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles gmail tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/google_drive/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/google_drive/agent.py index 091f431f3..e8601ab54 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/google_drive/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/google_drive/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles google drive tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/jira/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/jira/agent.py index 8e606a129..a21662eb5 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/jira/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/jira/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles jira tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/linear/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/linear/agent.py index f95d07010..82fdb245f 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/linear/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/linear/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles linear tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/luma/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/luma/agent.py index 7b53d4edd..81a95270b 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/luma/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/luma/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles luma tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/notion/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/notion/agent.py index 7d15e5cc0..9ff3105b4 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/notion/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/notion/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles notion tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/onedrive/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/onedrive/agent.py index 698a5ce5f..edf90c1ea 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/onedrive/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/onedrive/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles onedrive tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/slack/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/slack/agent.py index 31de5e2f2..aa8198827 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/slack/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/slack/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles slack tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/teams/agent.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/teams/agent.py index 9d09bcf65..b8fc5d1ba 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/teams/agent.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/connectors/teams/agent.py @@ -14,6 +14,7 @@ from app.agents.multi_agent_with_deepagents.subagents.shared.md_file_reader impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolsPermissions, merge_tools_permissions, + middleware_gated_interrupt_on, ) from app.agents.multi_agent_with_deepagents.subagents.shared.subagent_builder import ( pack_subagent, @@ -38,7 +39,7 @@ def build_subagent( for row in (*merged_tools_bucket["allow"], *merged_tools_bucket["ask"]) if row.get("tool") is not None ] - interrupt_on = {r["name"]: True for r in merged_tools_bucket["ask"] if r.get("name")} + interrupt_on = middleware_gated_interrupt_on(merged_tools_bucket) description = read_md_file(__package__, "description").strip() if not description: description = "Handles teams tasks for this workspace." diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/mcp_tools/index.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/mcp_tools/index.py index 087110974..1e635cae5 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/mcp_tools/index.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/mcp_tools/index.py @@ -21,7 +21,7 @@ from app.agents.multi_agent_with_deepagents.subagents.mcp_tools.permissions impo from app.agents.multi_agent_with_deepagents.subagents.shared.permissions import ( ToolPermissionItem, ToolsPermissions, - tool_permission_row, + mcp_tool_permission_row, ) from app.agents.new_chat.tools.mcp_tool import load_mcp_tools from app.db import SearchSourceConnector @@ -125,15 +125,15 @@ def _split_tools_by_permissions( for t in tools: meta: dict[str, Any] = getattr(t, "metadata", None) or {} if meta.get("hitl") is False: - allow.append(tool_permission_row(t)) + allow.append(mcp_tool_permission_row(t)) continue key = _get_mcp_tool_name(t) if key in allow_names: - allow.append(tool_permission_row(t)) + allow.append(mcp_tool_permission_row(t)) elif key in ask_names: - ask.append(tool_permission_row(t)) + ask.append(mcp_tool_permission_row(t)) else: - ask.append(tool_permission_row(t)) + ask.append(mcp_tool_permission_row(t)) return {"allow": allow, "ask": ask} @@ -143,8 +143,14 @@ async def load_mcp_tools_by_connector( session: AsyncSession, search_space_id: int, ) -> dict[str, ToolsPermissions]: - """Load MCP tools and split rows using ``TOOLS_PERMISSIONS_BY_AGENT`` name sets.""" - flat = await load_mcp_tools(session, search_space_id) + """Load MCP tools and split rows using ``TOOLS_PERMISSIONS_BY_AGENT`` name sets. + + Pass ``bypass_internal_hitl=True`` so the subagent's + ``HumanInTheLoopMiddleware`` is the single HITL gate. + """ + flat = await load_mcp_tools( + session, search_space_id, bypass_internal_hitl=True + ) id_map, name_map = await fetch_mcp_connector_metadata_maps(session, search_space_id) buckets = partition_mcp_tools_by_connector(flat, id_map, name_map) return { diff --git a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/shared/permissions.py b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/shared/permissions.py index 20a9433e7..649478485 100644 --- a/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/shared/permissions.py +++ b/surfsense_backend/app/agents/multi_agent_with_deepagents/subagents/shared/permissions.py @@ -2,16 +2,21 @@ from __future__ import annotations -from typing import NotRequired, TypedDict +from typing import Literal, NotRequired, TypedDict from langchain_core.tools import BaseTool +# ``native`` rows self-gate via ``request_approval`` in the tool body; +# ``mcp`` rows are gated by ``HumanInTheLoopMiddleware`` via ``interrupt_on``. +ToolKind = Literal["native", "mcp"] + class ToolPermissionItem(TypedDict): - """``name`` is always set; ``tool`` is present when a bound tool exists.""" + """``name`` is always set; ``tool`` is present when a bound tool exists; ``kind`` defaults to ``native`` when absent.""" name: str tool: NotRequired[BaseTool] + kind: NotRequired[ToolKind] class ToolsPermissions(TypedDict): @@ -26,6 +31,11 @@ def tool_permission_row(tool: BaseTool) -> ToolPermissionItem: return {"name": getattr(tool, "name", "") or "", "tool": tool} +def mcp_tool_permission_row(tool: BaseTool) -> ToolPermissionItem: + """Build one allow/ask row tagged ``kind="mcp"`` so it routes through ``HumanInTheLoopMiddleware``.""" + return {"name": getattr(tool, "name", "") or "", "tool": tool, "kind": "mcp"} + + def merge_tools_permissions( base: ToolsPermissions, extra: ToolsPermissions | None, @@ -37,3 +47,14 @@ def merge_tools_permissions( "allow": [*base["allow"], *extra["allow"]], "ask": [*base["ask"], *extra["ask"]], } + + +def middleware_gated_interrupt_on( + bucket: ToolsPermissions, +) -> dict[str, bool]: + """``interrupt_on`` for ``ask`` rows whose bodies don't self-gate via ``request_approval``.""" + return { + r["name"]: True + for r in bucket["ask"] + if r.get("name") and r.get("kind") == "mcp" + }