mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-07 14:52:39 +02:00
refactor(multi-agent): introduce shared flags helper and permissions package
This commit is contained in:
parent
9a4ee5d16b
commit
a6df944247
5 changed files with 141 additions and 0 deletions
|
|
@ -0,0 +1,10 @@
|
||||||
|
"""Single source of truth for the feature-flag predicate."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from app.agents.new_chat.feature_flags import AgentFeatureFlags
|
||||||
|
|
||||||
|
|
||||||
|
def enabled(flags: AgentFeatureFlags, attr: str) -> bool:
|
||||||
|
"""``flags.<attr>`` is on AND the new-agent-stack kill switch is off."""
|
||||||
|
return getattr(flags, attr) and not flags.disable_new_agent_stack
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
"""Permission rulesets fanned out to parent / general-purpose / subagent stacks."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .context import PermissionContext, build_permission_context
|
||||||
|
from .middleware import build_full_permission_mw
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"PermissionContext",
|
||||||
|
"build_full_permission_mw",
|
||||||
|
"build_permission_context",
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,109 @@
|
||||||
|
"""Derive shared permission context once; fan out to all three stack layers.
|
||||||
|
|
||||||
|
The context carries:
|
||||||
|
- ``rulesets``: full ask/deny/allow rules for the main-agent permission middleware.
|
||||||
|
- ``general_purpose_interrupt_on``: ``ask`` rules mirrored as deepagents
|
||||||
|
``interrupt_on`` so HITL still triggers from inside ``task`` runs (subagents
|
||||||
|
bypass the main-agent permission middleware).
|
||||||
|
- ``subagent_deny_mw``: a deny-only ``PermissionMiddleware`` instance shared
|
||||||
|
across the general-purpose and registry subagent stacks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Sequence
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from langchain_core.tools import BaseTool
|
||||||
|
|
||||||
|
from app.agents.new_chat.feature_flags import AgentFeatureFlags
|
||||||
|
from app.agents.new_chat.filesystem_selection import FilesystemMode
|
||||||
|
from app.agents.new_chat.middleware import PermissionMiddleware
|
||||||
|
from app.agents.new_chat.permissions import Rule, Ruleset
|
||||||
|
from app.agents.new_chat.tools.registry import BUILTIN_TOOLS
|
||||||
|
|
||||||
|
from ..flags import enabled
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class PermissionContext:
|
||||||
|
rulesets: list[Ruleset]
|
||||||
|
general_purpose_interrupt_on: dict[str, bool]
|
||||||
|
subagent_deny_mw: PermissionMiddleware | None
|
||||||
|
|
||||||
|
|
||||||
|
def build_permission_context(
|
||||||
|
*,
|
||||||
|
flags: AgentFeatureFlags,
|
||||||
|
filesystem_mode: FilesystemMode,
|
||||||
|
tools: Sequence[BaseTool],
|
||||||
|
available_connectors: list[str] | None,
|
||||||
|
) -> PermissionContext:
|
||||||
|
is_desktop_fs = filesystem_mode == FilesystemMode.DESKTOP_LOCAL_FOLDER
|
||||||
|
permission_enabled = enabled(flags, "enable_permission")
|
||||||
|
|
||||||
|
rulesets: list[Ruleset] = []
|
||||||
|
if permission_enabled or is_desktop_fs:
|
||||||
|
rulesets.append(
|
||||||
|
Ruleset(
|
||||||
|
rules=[Rule(permission="*", pattern="*", action="allow")],
|
||||||
|
origin="surfsense_defaults",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if is_desktop_fs:
|
||||||
|
rulesets.append(
|
||||||
|
Ruleset(
|
||||||
|
rules=[
|
||||||
|
Rule(permission="rm", pattern="*", action="ask"),
|
||||||
|
Rule(permission="rmdir", pattern="*", action="ask"),
|
||||||
|
Rule(permission="move_file", pattern="*", action="ask"),
|
||||||
|
Rule(permission="edit_file", pattern="*", action="ask"),
|
||||||
|
Rule(permission="write_file", pattern="*", action="ask"),
|
||||||
|
],
|
||||||
|
origin="desktop_safety",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
tool_names_in_use = {t.name for t in tools}
|
||||||
|
|
||||||
|
if permission_enabled:
|
||||||
|
available_set = set(available_connectors or [])
|
||||||
|
synthesized: list[Rule] = []
|
||||||
|
for tool_def in BUILTIN_TOOLS:
|
||||||
|
if tool_def.name not in tool_names_in_use:
|
||||||
|
continue
|
||||||
|
rc = tool_def.required_connector
|
||||||
|
if rc and rc not in available_set:
|
||||||
|
synthesized.append(
|
||||||
|
Rule(permission=tool_def.name, pattern="*", action="deny")
|
||||||
|
)
|
||||||
|
if synthesized:
|
||||||
|
rulesets.append(
|
||||||
|
Ruleset(rules=synthesized, origin="connector_synthesized")
|
||||||
|
)
|
||||||
|
|
||||||
|
general_purpose_interrupt_on: dict[str, bool] = {
|
||||||
|
rule.permission: True
|
||||||
|
for rs in rulesets
|
||||||
|
for rule in rs.rules
|
||||||
|
if rule.action == "ask" and rule.permission in tool_names_in_use
|
||||||
|
}
|
||||||
|
|
||||||
|
deny_rulesets = [
|
||||||
|
Ruleset(
|
||||||
|
rules=[r for r in rs.rules if r.action == "deny"],
|
||||||
|
origin=rs.origin,
|
||||||
|
)
|
||||||
|
for rs in rulesets
|
||||||
|
]
|
||||||
|
deny_rulesets = [rs for rs in deny_rulesets if rs.rules]
|
||||||
|
|
||||||
|
subagent_deny_mw: PermissionMiddleware | None = (
|
||||||
|
PermissionMiddleware(rulesets=deny_rulesets) if deny_rulesets else None
|
||||||
|
)
|
||||||
|
|
||||||
|
return PermissionContext(
|
||||||
|
rulesets=rulesets,
|
||||||
|
general_purpose_interrupt_on=general_purpose_interrupt_on,
|
||||||
|
subagent_deny_mw=subagent_deny_mw,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
"""Main-agent permission middleware (full ask/deny/allow rules)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from app.agents.new_chat.middleware import PermissionMiddleware
|
||||||
|
from app.agents.new_chat.permissions import Ruleset
|
||||||
|
|
||||||
|
|
||||||
|
def build_full_permission_mw(rulesets: list[Ruleset]) -> PermissionMiddleware | None:
|
||||||
|
return PermissionMiddleware(rulesets=rulesets) if rulesets else None
|
||||||
Loading…
Add table
Add a link
Reference in a new issue