Add main-agent tool allowlist plus permission and prune helpers.

This commit is contained in:
CREDO23 2026-05-01 23:17:46 +02:00
parent d9c873b2e1
commit 083a9f7946
9 changed files with 153 additions and 0 deletions

View file

@ -0,0 +1,5 @@
"""SurfSense main-agent package (factory export added when runtime lands)."""
from __future__ import annotations
__all__: list[str] = []

View file

@ -0,0 +1,7 @@
"""Tool-name pruning for context editing (exclude lists without dropping protected tools)."""
from __future__ import annotations
from .prune_tool_names import PRUNE_PROTECTED_TOOL_NAMES, safe_exclude_tools
__all__ = ["PRUNE_PROTECTED_TOOL_NAMES", "safe_exclude_tools"]

View file

@ -0,0 +1,26 @@
"""Tool names excluded from context-editing prune when bound."""
from __future__ import annotations
from collections.abc import Sequence
from langchain_core.tools import BaseTool
PRUNE_PROTECTED_TOOL_NAMES: frozenset[str] = frozenset(
{
"generate_report",
"generate_resume",
"generate_podcast",
"generate_video_presentation",
"generate_image",
"read_email",
"search_emails",
"invalid",
},
)
def safe_exclude_tools(tools: Sequence[BaseTool]) -> tuple[str, ...]:
"""Names from ``PRUNE_PROTECTED_TOOL_NAMES`` that appear in ``tools``."""
enabled = {t.name for t in tools}
return tuple(n for n in PRUNE_PROTECTED_TOOL_NAMES if n in enabled)

View file

@ -0,0 +1,13 @@
"""Connector-gated tool deny rules and small permission helpers for the main-agent graph."""
from __future__ import annotations
from .connector_deny_rules import synthesize_connector_deny_rules
from .connector_gated_tool_names import iter_connector_gated_tools
from .rule import Rule
__all__ = [
"Rule",
"iter_connector_gated_tools",
"synthesize_connector_deny_rules",
]

View file

@ -0,0 +1,21 @@
"""Synthesized PermissionMiddleware deny rules for tools gated by connector."""
from __future__ import annotations
from .connector_gated_tool_names import iter_connector_gated_tools
from .rule import Rule
def synthesize_connector_deny_rules(
*,
available_connectors: list[str] | None,
enabled_tool_names: set[str],
) -> list[Rule]:
available = set(available_connectors or [])
deny: list[Rule] = []
for name, required in iter_connector_gated_tools():
if name not in enabled_tool_names:
continue
if required not in available:
deny.append(Rule(permission=name, pattern="*", action="deny"))
return deny

View file

@ -0,0 +1,42 @@
"""Tool name → required searchable connector type (keep in sync with new_chat ``BUILTIN_TOOLS``)."""
from __future__ import annotations
# Synced from ``app.agents.new_chat.tools.registry`` ToolDefinition.required_connector entries.
_CONNECTOR_GATED: tuple[tuple[str, str], ...] = (
("create_notion_page", "NOTION_CONNECTOR"),
("update_notion_page", "NOTION_CONNECTOR"),
("delete_notion_page", "NOTION_CONNECTOR"),
("create_google_drive_file", "GOOGLE_DRIVE_FILE"),
("delete_google_drive_file", "GOOGLE_DRIVE_FILE"),
("create_dropbox_file", "DROPBOX_FILE"),
("delete_dropbox_file", "DROPBOX_FILE"),
("create_onedrive_file", "ONEDRIVE_FILE"),
("delete_onedrive_file", "ONEDRIVE_FILE"),
("search_calendar_events", "GOOGLE_CALENDAR_CONNECTOR"),
("create_calendar_event", "GOOGLE_CALENDAR_CONNECTOR"),
("update_calendar_event", "GOOGLE_CALENDAR_CONNECTOR"),
("delete_calendar_event", "GOOGLE_CALENDAR_CONNECTOR"),
("search_gmail", "GOOGLE_GMAIL_CONNECTOR"),
("read_gmail_email", "GOOGLE_GMAIL_CONNECTOR"),
("create_gmail_draft", "GOOGLE_GMAIL_CONNECTOR"),
("send_gmail_email", "GOOGLE_GMAIL_CONNECTOR"),
("trash_gmail_email", "GOOGLE_GMAIL_CONNECTOR"),
("update_gmail_draft", "GOOGLE_GMAIL_CONNECTOR"),
("create_confluence_page", "CONFLUENCE_CONNECTOR"),
("update_confluence_page", "CONFLUENCE_CONNECTOR"),
("delete_confluence_page", "CONFLUENCE_CONNECTOR"),
("list_discord_channels", "DISCORD_CONNECTOR"),
("read_discord_messages", "DISCORD_CONNECTOR"),
("send_discord_message", "DISCORD_CONNECTOR"),
("list_teams_channels", "TEAMS_CONNECTOR"),
("read_teams_messages", "TEAMS_CONNECTOR"),
("send_teams_message", "TEAMS_CONNECTOR"),
("list_luma_events", "LUMA_CONNECTOR"),
("read_luma_event", "LUMA_CONNECTOR"),
("create_luma_event", "LUMA_CONNECTOR"),
)
def iter_connector_gated_tools() -> tuple[tuple[str, str], ...]:
return _CONNECTOR_GATED

View file

@ -0,0 +1,15 @@
"""Minimal permission rule type (mirrors OpenCode semantics used by PermissionMiddleware)."""
from __future__ import annotations
from dataclasses import dataclass
from typing import Literal
RuleAction = Literal["allow", "deny", "ask"]
@dataclass(frozen=True)
class Rule:
permission: str
pattern: str
action: RuleAction

View file

@ -0,0 +1,7 @@
"""Main-agent SurfSense tool allowlist."""
from __future__ import annotations
from .index import MAIN_AGENT_SURFSENSE_TOOL_NAMES, MAIN_AGENT_SURFSENSE_TOOL_NAMES_ORDERED
__all__ = ["MAIN_AGENT_SURFSENSE_TOOL_NAMES", "MAIN_AGENT_SURFSENSE_TOOL_NAMES_ORDERED"]

View file

@ -0,0 +1,17 @@
"""Main-agent SurfSense builtin tool names (not full ``new_chat``).
Connector integrations, MCP, deliverables, etc. are delegated via ``task`` subagents.
"""
from __future__ import annotations
MAIN_AGENT_SURFSENSE_TOOL_NAMES_ORDERED: tuple[str, ...] = (
"search_surfsense_docs",
"web_search",
"scrape_webpage",
"update_memory",
)
MAIN_AGENT_SURFSENSE_TOOL_NAMES: frozenset[str] = frozenset(
MAIN_AGENT_SURFSENSE_TOOL_NAMES_ORDERED,
)