mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-19 18:45:15 +02:00
multi_agent_chat: scope MCP allow/ask permissions per subagent + drop "policy" synonym
This commit is contained in:
parent
0723702320
commit
67142e68b1
21 changed files with 117 additions and 180 deletions
|
|
@ -9,7 +9,7 @@ matching OpenCode's ``permission/index.ts`` evaluation order):
|
||||||
needs to *deny* what the user has explicitly forbidden; the default
|
needs to *deny* what the user has explicitly forbidden; the default
|
||||||
``ask`` fallback would otherwise double-prompt every safe read-only
|
``ask`` fallback would otherwise double-prompt every safe read-only
|
||||||
call.
|
call.
|
||||||
2. ``extra_rulesets`` — caller-supplied policies. The KB subagent contributes
|
2. ``extra_rulesets`` — caller-supplied rulesets. The KB subagent contributes
|
||||||
its destructive-FS ``ask`` rules here; connectors will follow once
|
its destructive-FS ``ask`` rules here; connectors will follow once
|
||||||
they migrate off ``interrupt_on``.
|
they migrate off ``interrupt_on``.
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ def build_permission_mw(
|
||||||
flags: Feature toggles. ``enable_permission`` switches the engine on;
|
flags: Feature toggles. ``enable_permission`` switches the engine on;
|
||||||
``disable_new_agent_stack`` overrides everything for safety.
|
``disable_new_agent_stack`` overrides everything for safety.
|
||||||
extra_rulesets: Caller-supplied rulesets layered after the defaults.
|
extra_rulesets: Caller-supplied rulesets layered after the defaults.
|
||||||
Subagents pass their own policy here so each subagent owns its
|
Subagents pass their own ruleset here so each subagent owns its
|
||||||
rules without aliasing a shared engine. Presence of any extra
|
rules without aliasing a shared engine. Presence of any extra
|
||||||
ruleset forces the middleware on regardless of
|
ruleset forces the middleware on regardless of
|
||||||
``enable_permission`` — an explicit ``ask`` rule always asks.
|
``enable_permission`` — an explicit ``ask`` rule always asks.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"""``knowledge_base`` route: full and read-only ``SurfSenseSubagentSpec`` builders.
|
"""``knowledge_base`` route: full and read-only ``SurfSenseSubagentSpec`` builders.
|
||||||
|
|
||||||
KB owns the destructive-FS approval policy: rules live in :data:`KB_RULESET`
|
KB owns its destructive-FS approval ruleset (:data:`KB_RULESET`); rules
|
||||||
and are layered into KB's :class:`PermissionMiddleware` (built inside
|
are layered into KB's :class:`PermissionMiddleware` (built inside
|
||||||
``build_kb_middleware``). The legacy ``interrupt_on`` kwarg is gone — one
|
``build_kb_middleware``). The legacy ``interrupt_on`` kwarg is gone — one
|
||||||
emitter, one wire format, one source of truth.
|
emitter, one wire format, one source of truth.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ def build_kb_middleware(
|
||||||
) -> list[Any]:
|
) -> list[Any]:
|
||||||
"""Compose the KB subagent's middleware list.
|
"""Compose the KB subagent's middleware list.
|
||||||
|
|
||||||
``ruleset`` is the KB-owned permission policy (typically the
|
``ruleset`` is the KB-owned permission ruleset (typically the
|
||||||
destructive-FS ask rules). When provided, a dedicated
|
destructive-FS ask rules). When provided, a dedicated
|
||||||
:class:`PermissionMiddleware` is appended so KB enforces approval at
|
:class:`PermissionMiddleware` is appended so KB enforces approval at
|
||||||
the rule layer instead of the legacy ``interrupt_on`` kwarg.
|
the rule layer instead of the legacy ``interrupt_on`` kwarg.
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
"""Route-local tool policy for the ``knowledge_base`` subagent."""
|
"""Route-local tool permissions for the ``knowledge_base`` subagent."""
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Route-local FS tool policy.
|
"""Route-local FS tool permissions.
|
||||||
|
|
||||||
The KB subagent's actual ``BaseTool`` instances are provided at runtime by
|
The KB subagent's actual ``BaseTool`` instances are provided at runtime by
|
||||||
``SurfSenseFilesystemMiddleware`` (mounted in ``agent.py``). This module
|
``SurfSenseFilesystemMiddleware`` (mounted in ``agent.py``). This module
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,17 @@ def load_tools(
|
||||||
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
||||||
) -> ToolsPermissions:
|
) -> ToolsPermissions:
|
||||||
_ = {**(dependencies or {}), **kwargs}
|
_ = {**(dependencies or {}), **kwargs}
|
||||||
return {"allow": [], "ask": []}
|
return {
|
||||||
|
"allow": [
|
||||||
|
{"name": "list_bases"},
|
||||||
|
{"name": "search_bases"},
|
||||||
|
{"name": "list_tables_for_base"},
|
||||||
|
{"name": "get_table_schema"},
|
||||||
|
{"name": "list_records_for_table"},
|
||||||
|
{"name": "search_records"},
|
||||||
|
],
|
||||||
|
"ask": [
|
||||||
|
{"name": "create_records_for_table"},
|
||||||
|
{"name": "update_records_for_table"},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,16 @@ def load_tools(
|
||||||
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
||||||
) -> ToolsPermissions:
|
) -> ToolsPermissions:
|
||||||
_ = {**(dependencies or {}), **kwargs}
|
_ = {**(dependencies or {}), **kwargs}
|
||||||
return {"allow": [], "ask": []}
|
return {
|
||||||
|
"allow": [
|
||||||
|
{"name": "clickup_search"},
|
||||||
|
{"name": "clickup_get_task"},
|
||||||
|
{"name": "clickup_get_workspace_hierarchy"},
|
||||||
|
{"name": "clickup_get_list"},
|
||||||
|
{"name": "clickup_find_member_by_name"},
|
||||||
|
],
|
||||||
|
"ask": [
|
||||||
|
{"name": "clickup_create_task"},
|
||||||
|
{"name": "clickup_update_task"},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,20 @@ def load_tools(
|
||||||
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
||||||
) -> ToolsPermissions:
|
) -> ToolsPermissions:
|
||||||
_ = {**(dependencies or {}), **kwargs}
|
_ = {**(dependencies or {}), **kwargs}
|
||||||
return {"allow": [], "ask": []}
|
return {
|
||||||
|
"allow": [
|
||||||
|
{"name": "getAccessibleAtlassianResources"},
|
||||||
|
{"name": "getVisibleJiraProjects"},
|
||||||
|
{"name": "searchJiraIssuesUsingJql"},
|
||||||
|
{"name": "getJiraIssue"},
|
||||||
|
{"name": "getJiraProjectIssueTypesMetadata"},
|
||||||
|
{"name": "getJiraIssueTypeMetaWithFields"},
|
||||||
|
{"name": "getTransitionsForJiraIssue"},
|
||||||
|
{"name": "lookupJiraAccountId"},
|
||||||
|
],
|
||||||
|
"ask": [
|
||||||
|
{"name": "createJiraIssue"},
|
||||||
|
{"name": "editJiraIssue"},
|
||||||
|
{"name": "transitionJiraIssue"},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,27 @@ def load_tools(
|
||||||
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
||||||
) -> ToolsPermissions:
|
) -> ToolsPermissions:
|
||||||
_ = {**(dependencies or {}), **kwargs}
|
_ = {**(dependencies or {}), **kwargs}
|
||||||
return {"allow": [], "ask": []}
|
return {
|
||||||
|
"allow": [
|
||||||
|
{"name": "list_issues"},
|
||||||
|
{"name": "get_issue"},
|
||||||
|
{"name": "list_my_issues"},
|
||||||
|
{"name": "list_issue_statuses"},
|
||||||
|
{"name": "list_issue_labels"},
|
||||||
|
{"name": "list_comments"},
|
||||||
|
{"name": "list_users"},
|
||||||
|
{"name": "get_user"},
|
||||||
|
{"name": "list_teams"},
|
||||||
|
{"name": "get_team"},
|
||||||
|
{"name": "list_projects"},
|
||||||
|
{"name": "get_project"},
|
||||||
|
{"name": "list_project_labels"},
|
||||||
|
{"name": "list_cycles"},
|
||||||
|
{"name": "list_documents"},
|
||||||
|
{"name": "get_document"},
|
||||||
|
{"name": "search_documentation"},
|
||||||
|
],
|
||||||
|
"ask": [
|
||||||
|
{"name": "save_issue"}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,15 @@ def load_tools(
|
||||||
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
*, dependencies: dict[str, Any] | None = None, **kwargs: Any
|
||||||
) -> ToolsPermissions:
|
) -> ToolsPermissions:
|
||||||
_ = {**(dependencies or {}), **kwargs}
|
_ = {**(dependencies or {}), **kwargs}
|
||||||
return {"allow": [], "ask": []}
|
return {
|
||||||
|
"allow": [
|
||||||
|
{"name": "slack_search_channels"},
|
||||||
|
{"name": "slack_search_messages"},
|
||||||
|
{"name": "slack_search_users"},
|
||||||
|
{"name": "slack_read_channel"},
|
||||||
|
{"name": "slack_read_thread"},
|
||||||
|
],
|
||||||
|
"ask": [
|
||||||
|
{"name": "slack_send_message"},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
"""Load MCP tools, partition by connector agent, apply allow/ask name rules."""
|
"""Load MCP tools, partition by connector agent, apply each subagent's allow/ask permissions."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.mcp_tools.permissions import (
|
|
||||||
TOOLS_PERMISSIONS_BY_AGENT,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .index import (
|
from .index import (
|
||||||
fetch_mcp_connector_metadata_maps,
|
fetch_mcp_connector_metadata_maps,
|
||||||
load_mcp_tools_by_connector,
|
load_mcp_tools_by_connector,
|
||||||
|
|
@ -13,7 +9,6 @@ from .index import (
|
||||||
)
|
)
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"TOOLS_PERMISSIONS_BY_AGENT",
|
|
||||||
"fetch_mcp_connector_metadata_maps",
|
"fetch_mcp_connector_metadata_maps",
|
||||||
"load_mcp_tools_by_connector",
|
"load_mcp_tools_by_connector",
|
||||||
"partition_mcp_tools_by_connector",
|
"partition_mcp_tools_by_connector",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Discover MCP tools, bucket by connector agent, apply allow/ask from policy."""
|
"""Discover MCP tools, bucket by connector agent, apply each subagent's allow/ask permissions."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -15,8 +15,20 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from app.agents.multi_agent_chat.constants import (
|
from app.agents.multi_agent_chat.constants import (
|
||||||
CONNECTOR_TYPE_TO_CONNECTOR_AGENT_MAPS,
|
CONNECTOR_TYPE_TO_CONNECTOR_AGENT_MAPS,
|
||||||
)
|
)
|
||||||
from app.agents.multi_agent_chat.subagents.mcp_tools.permissions import (
|
from app.agents.multi_agent_chat.subagents.connectors.airtable.tools.index import (
|
||||||
TOOLS_PERMISSIONS_BY_AGENT,
|
load_tools as _airtable_permissions,
|
||||||
|
)
|
||||||
|
from app.agents.multi_agent_chat.subagents.connectors.clickup.tools.index import (
|
||||||
|
load_tools as _clickup_permissions,
|
||||||
|
)
|
||||||
|
from app.agents.multi_agent_chat.subagents.connectors.jira.tools.index import (
|
||||||
|
load_tools as _jira_permissions,
|
||||||
|
)
|
||||||
|
from app.agents.multi_agent_chat.subagents.connectors.linear.tools.index import (
|
||||||
|
load_tools as _linear_permissions,
|
||||||
|
)
|
||||||
|
from app.agents.multi_agent_chat.subagents.connectors.slack.tools.index import (
|
||||||
|
load_tools as _slack_permissions,
|
||||||
)
|
)
|
||||||
from app.agents.multi_agent_chat.subagents.shared.hitl.approvals.middleware_gated import (
|
from app.agents.multi_agent_chat.subagents.shared.hitl.approvals.middleware_gated import (
|
||||||
middleware_gated_tool_permission_row,
|
middleware_gated_tool_permission_row,
|
||||||
|
|
@ -31,6 +43,15 @@ from app.db import SearchSourceConnector
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
_MCP_PERMISSIONS_BY_AGENT: dict[str, ToolsPermissions] = {
|
||||||
|
"airtable": _airtable_permissions(),
|
||||||
|
"clickup": _clickup_permissions(),
|
||||||
|
"jira": _jira_permissions(),
|
||||||
|
"linear": _linear_permissions(),
|
||||||
|
"slack": _slack_permissions(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
## Helper functions for fetching connector metadata maps
|
## Helper functions for fetching connector metadata maps
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -150,7 +171,7 @@ async def load_mcp_tools_by_connector(
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
search_space_id: int,
|
search_space_id: int,
|
||||||
) -> dict[str, ToolsPermissions]:
|
) -> dict[str, ToolsPermissions]:
|
||||||
"""Load MCP tools and split rows using ``TOOLS_PERMISSIONS_BY_AGENT`` name sets.
|
"""Load MCP tools and split rows per subagent's own allow/ask permissions.
|
||||||
|
|
||||||
Pass ``bypass_internal_hitl=True`` so the subagent's
|
Pass ``bypass_internal_hitl=True`` so the subagent's
|
||||||
``HumanInTheLoopMiddleware`` is the single HITL gate.
|
``HumanInTheLoopMiddleware`` is the single HITL gate.
|
||||||
|
|
@ -161,7 +182,7 @@ async def load_mcp_tools_by_connector(
|
||||||
return {
|
return {
|
||||||
agent: _split_tools_by_permissions(
|
agent: _split_tools_by_permissions(
|
||||||
tools,
|
tools,
|
||||||
TOOLS_PERMISSIONS_BY_AGENT.get(agent, {"allow": [], "ask": []}),
|
_MCP_PERMISSIONS_BY_AGENT.get(agent, {"allow": [], "ask": []}),
|
||||||
)
|
)
|
||||||
for agent, tools in buckets.items()
|
for agent, tools in buckets.items()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
"""Bundled MCP allow/ask name rows per connector agent (MCP-backed routes only)."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
|
|
||||||
ToolsPermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .airtable import TOOLS_PERMISSIONS as _AIRTABLE
|
|
||||||
from .clickup import TOOLS_PERMISSIONS as _CLICKUP
|
|
||||||
from .jira import TOOLS_PERMISSIONS as _JIRA
|
|
||||||
from .linear import TOOLS_PERMISSIONS as _LINEAR
|
|
||||||
from .slack import TOOLS_PERMISSIONS as _SLACK
|
|
||||||
|
|
||||||
TOOLS_PERMISSIONS_BY_AGENT: dict[str, ToolsPermissions] = {
|
|
||||||
"airtable": _AIRTABLE,
|
|
||||||
"clickup": _CLICKUP,
|
|
||||||
"jira": _JIRA,
|
|
||||||
"linear": _LINEAR,
|
|
||||||
"slack": _SLACK,
|
|
||||||
}
|
|
||||||
|
|
||||||
__all__ = ["TOOLS_PERMISSIONS_BY_AGENT"]
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
"""Airtable MCP: which server tool names are allow vs ask."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
|
|
||||||
ToolsPermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
TOOLS_PERMISSIONS: ToolsPermissions = {
|
|
||||||
"allow": [
|
|
||||||
{"name": "list_bases"},
|
|
||||||
{"name": "search_bases"},
|
|
||||||
{"name": "list_tables_for_base"},
|
|
||||||
{"name": "get_table_schema"},
|
|
||||||
{"name": "list_records_for_table"},
|
|
||||||
{"name": "search_records"},
|
|
||||||
],
|
|
||||||
"ask": [
|
|
||||||
{"name": "create_records_for_table"},
|
|
||||||
{"name": "update_records_for_table"},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
"""ClickUp MCP: which server tool names are allow vs ask."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
|
|
||||||
ToolsPermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
TOOLS_PERMISSIONS: ToolsPermissions = {
|
|
||||||
"allow": [
|
|
||||||
{"name": "clickup_search"},
|
|
||||||
{"name": "clickup_get_task"},
|
|
||||||
{"name": "clickup_get_workspace_hierarchy"},
|
|
||||||
{"name": "clickup_get_list"},
|
|
||||||
{"name": "clickup_find_member_by_name"},
|
|
||||||
],
|
|
||||||
"ask": [
|
|
||||||
{"name": "clickup_create_task"},
|
|
||||||
{"name": "clickup_update_task"},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
"""Re-exports permission row types for MCP policy modules."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
|
|
||||||
ToolPermissionItem,
|
|
||||||
ToolsPermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = ["ToolPermissionItem", "ToolsPermissions"]
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
"""Jira MCP: which server tool names are allow vs ask."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
|
|
||||||
ToolsPermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
TOOLS_PERMISSIONS: ToolsPermissions = {
|
|
||||||
"allow": [
|
|
||||||
{"name": "getAccessibleAtlassianResources"},
|
|
||||||
{"name": "getVisibleJiraProjects"},
|
|
||||||
{"name": "searchJiraIssuesUsingJql"},
|
|
||||||
{"name": "getJiraIssue"},
|
|
||||||
{"name": "getJiraProjectIssueTypesMetadata"},
|
|
||||||
{"name": "getJiraIssueTypeMetaWithFields"},
|
|
||||||
{"name": "getTransitionsForJiraIssue"},
|
|
||||||
{"name": "lookupJiraAccountId"},
|
|
||||||
],
|
|
||||||
"ask": [
|
|
||||||
{"name": "createJiraIssue"},
|
|
||||||
{"name": "editJiraIssue"},
|
|
||||||
{"name": "transitionJiraIssue"},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
"""Linear MCP: which server tool names are allow vs ask."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
|
|
||||||
ToolsPermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
_TOOLS_ALLOW = (
|
|
||||||
"list_issues",
|
|
||||||
"get_issue",
|
|
||||||
"list_my_issues",
|
|
||||||
"list_issue_statuses",
|
|
||||||
"list_issue_labels",
|
|
||||||
"list_comments",
|
|
||||||
"list_users",
|
|
||||||
"get_user",
|
|
||||||
"list_teams",
|
|
||||||
"get_team",
|
|
||||||
"list_projects",
|
|
||||||
"get_project",
|
|
||||||
"list_project_labels",
|
|
||||||
"list_cycles",
|
|
||||||
"list_documents",
|
|
||||||
"get_document",
|
|
||||||
"search_documentation",
|
|
||||||
)
|
|
||||||
|
|
||||||
TOOLS_PERMISSIONS: ToolsPermissions = {
|
|
||||||
"allow": [{"name": n} for n in _TOOLS_ALLOW],
|
|
||||||
"ask": [{"name": "save_issue"}],
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
"""Slack MCP: which server tool names are allow vs ask."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
|
|
||||||
ToolsPermissions,
|
|
||||||
)
|
|
||||||
|
|
||||||
TOOLS_PERMISSIONS: ToolsPermissions = {
|
|
||||||
"allow": [
|
|
||||||
{"name": "slack_search_channels"},
|
|
||||||
{"name": "slack_search_messages"},
|
|
||||||
{"name": "slack_search_users"},
|
|
||||||
{"name": "slack_read_channel"},
|
|
||||||
{"name": "slack_read_thread"},
|
|
||||||
],
|
|
||||||
"ask": [
|
|
||||||
{"name": "slack_send_message"},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
"""SurfSense's subagent contribution: deepagents spec + permission policy.
|
"""SurfSense's subagent contribution: deepagents spec + permission ruleset."""
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -19,7 +18,7 @@ class SurfSenseSubagentSpec:
|
||||||
only fields ``deepagents.SubAgent`` recognises.
|
only fields ``deepagents.SubAgent`` recognises.
|
||||||
ruleset: Permission rules this subagent contributes. The orchestrator
|
ruleset: Permission rules this subagent contributes. The orchestrator
|
||||||
layers them into the subagent's :class:`PermissionMiddleware`,
|
layers them into the subagent's :class:`PermissionMiddleware`,
|
||||||
so each subagent owns its own policy without aliasing the
|
so each subagent owns its own ruleset without aliasing the
|
||||||
shared rule engine.
|
shared rule engine.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class ToolPermissionItem(TypedDict):
|
||||||
"""One allow/ask row.
|
"""One allow/ask row.
|
||||||
|
|
||||||
``name`` is always set; ``tool`` is present when a bound BaseTool exists
|
``name`` is always set; ``tool`` is present when a bound BaseTool exists
|
||||||
(absent for name-only MCP policy rows). ``kind`` defaults to
|
(absent for name-only MCP allow/ask rows). ``kind`` defaults to
|
||||||
``self_gated`` when absent so existing connector factories keep working
|
``self_gated`` when absent so existing connector factories keep working
|
||||||
without explicit tagging.
|
without explicit tagging.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue