From a975754e7db501f0da60b497eb32f62ba1f1799b Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 4 Jun 2026 12:23:12 +0200 Subject: [PATCH] refactor(agents): move feature flags to app/agents/shared/feature_flags (slice 2b) Promote the agent feature-flag resolver (AgentFeatureFlags / get_flags) out of `new_chat` into the cross-agent `app/agents/shared` kernel. feature_flags is a pure leaf consumed across the multi-agent middleware stack, the chat routes, and tests. Moved it via git mv (content unchanged) and flipped all 37 importers to app.agents.shared.feature_flags. A thin re-export shim remains at new_chat/feature_flags.py only for the not-yet-retired single-agent (chat_deepagent); it goes away with the single-agent deletion. Behavior-preserving: only import paths change. 1243 tests green. --- .../main_agent/graph/compile_graph_sync.py | 2 +- .../main_agent/runtime/agent_cache.py | 2 +- .../main_agent/runtime/factory.py | 2 +- .../middleware/main_agent/action_log.py | 2 +- .../middleware/main_agent/busy_mutex.py | 2 +- .../middleware/main_agent/context_editing.py | 2 +- .../middleware/main_agent/doom_loop.py | 2 +- .../middleware/main_agent/noop_injection.py | 2 +- .../middleware/main_agent/otel.py | 2 +- .../middleware/main_agent/plugins.py | 2 +- .../middleware/main_agent/repair.py | 2 +- .../middleware/main_agent/skills.py | 2 +- .../middleware/shared/flags.py | 2 +- .../shared/permissions/middleware/factory.py | 2 +- .../middleware/shared/resilience/bundle.py | 2 +- .../middleware/shared/resilience/fallback.py | 2 +- .../shared/resilience/model_call_limit.py | 2 +- .../middleware/shared/resilience/retry.py | 2 +- .../shared/resilience/tool_call_limit.py | 2 +- .../multi_agent_chat/middleware/stack.py | 2 +- .../middleware/subagent/middleware_stack.py | 2 +- .../knowledge_base/middleware_stack.py | 2 +- .../app/agents/new_chat/feature_flags.py | 257 +----------------- .../agents/new_chat/middleware/action_log.py | 2 +- .../new_chat/middleware/kb_persistence.py | 2 +- .../new_chat/middleware/knowledge_search.py | 2 +- .../app/agents/shared/feature_flags.py | 257 ++++++++++++++++++ .../app/routes/agent_action_log_route.py | 2 +- .../app/routes/agent_flags_route.py | 2 +- .../app/routes/agent_permissions_route.py | 2 +- .../app/routes/agent_revert_route.py | 2 +- .../test_permission_ask_mcp_context.py | 2 +- .../test_subagent_owned_ruleset.py | 2 +- .../test_trusted_tool_save_on_always.py | 2 +- .../subagents/shared/test_subagent_builder.py | 2 +- .../unit/agents/new_chat/test_action_log.py | 2 +- .../agents/new_chat/test_feature_flags.py | 2 +- .../unit/routes/test_revert_turn_route.py | 2 +- 38 files changed, 304 insertions(+), 282 deletions(-) create mode 100644 surfsense_backend/app/agents/shared/feature_flags.py diff --git a/surfsense_backend/app/agents/multi_agent_chat/main_agent/graph/compile_graph_sync.py b/surfsense_backend/app/agents/multi_agent_chat/main_agent/graph/compile_graph_sync.py index 4de4e9cfe..2acbe0314 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/main_agent/graph/compile_graph_sync.py +++ b/surfsense_backend/app/agents/multi_agent_chat/main_agent/graph/compile_graph_sync.py @@ -15,7 +15,7 @@ from app.agents.multi_agent_chat.middleware.stack import ( build_main_agent_deepagent_middleware, ) from app.agents.shared.context import SurfSenseContextSchema -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.filesystem_selection import FilesystemMode from app.db import ChatVisibility diff --git a/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/agent_cache.py b/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/agent_cache.py index df1ee1b4c..ac950b3de 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/agent_cache.py +++ b/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/agent_cache.py @@ -17,7 +17,7 @@ from app.agents.new_chat.agent_cache import ( system_prompt_hash, tools_signature, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.filesystem_selection import FilesystemMode from app.db import ChatVisibility diff --git a/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py b/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py index 89e40af54..caafe4e5d 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py +++ b/surfsense_backend/app/agents/multi_agent_chat/main_agent/runtime/factory.py @@ -22,7 +22,7 @@ from app.agents.multi_agent_chat.subagents.mcp_tools.index import ( from app.agents.new_chat.connector_searchable_types import ( map_connectors_to_searchable_types, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags, get_flags +from app.agents.shared.feature_flags import AgentFeatureFlags, get_flags from app.agents.new_chat.filesystem_backends import build_backend_resolver from app.agents.new_chat.filesystem_selection import FilesystemMode, FilesystemSelection from app.agents.new_chat.llm_config import AgentConfig diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/action_log.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/action_log.py index c9f893d97..d1fa31512 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/action_log.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/action_log.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import ActionLogMiddleware from app.agents.new_chat.tools.registry import BUILTIN_TOOLS diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/busy_mutex.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/busy_mutex.py index 0ea53bf16..56147d850 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/busy_mutex.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/busy_mutex.py @@ -2,7 +2,7 @@ from __future__ import annotations -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import BusyMutexMiddleware from ..shared.flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/context_editing.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/context_editing.py index e8f99933e..82fca9f14 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/context_editing.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/context_editing.py @@ -10,7 +10,7 @@ from langchain_core.tools import BaseTool from app.agents.multi_agent_chat.main_agent.context_prune.prune_tool_names import ( safe_exclude_tools, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import ( ClearToolUsesEdit, SpillingContextEditingMiddleware, diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/doom_loop.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/doom_loop.py index d67b8d518..f1a82c206 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/doom_loop.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/doom_loop.py @@ -2,7 +2,7 @@ from __future__ import annotations -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import DoomLoopMiddleware from ..shared.flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/noop_injection.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/noop_injection.py index 6e6467ad0..f5371d0e1 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/noop_injection.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/noop_injection.py @@ -2,7 +2,7 @@ from __future__ import annotations -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import NoopInjectionMiddleware from ..shared.flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/otel.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/otel.py index bd7516e65..73b04672b 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/otel.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/otel.py @@ -2,7 +2,7 @@ from __future__ import annotations -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import OtelSpanMiddleware from ..shared.flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/plugins.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/plugins.py index 4418e3806..d3be13bfd 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/plugins.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/plugins.py @@ -7,7 +7,7 @@ from typing import Any from langchain_core.language_models import BaseChatModel -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.plugin_loader import ( PluginContext, load_allowed_plugin_names_from_env, diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/repair.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/repair.py index 378b61be1..e4ecdd0ed 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/repair.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/repair.py @@ -6,7 +6,7 @@ from collections.abc import Sequence from langchain_core.tools import BaseTool -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import ToolCallNameRepairMiddleware from ..shared.flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/skills.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/skills.py index 63a57c5a0..71aa952fb 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/skills.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/main_agent/skills.py @@ -6,7 +6,7 @@ import logging from deepagents.middleware.skills import SkillsMiddleware -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.filesystem_selection import FilesystemMode from app.agents.new_chat.middleware import ( build_skills_backend_factory, diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/flags.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/flags.py index 69994ae00..bf0365c45 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/flags.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/flags.py @@ -2,7 +2,7 @@ from __future__ import annotations -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags def enabled(flags: AgentFeatureFlags, attr: str) -> bool: diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/permissions/middleware/factory.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/permissions/middleware/factory.py index 3c061ded6..ed42c5822 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/permissions/middleware/factory.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/permissions/middleware/factory.py @@ -27,7 +27,7 @@ from collections.abc import Sequence from langchain_core.tools import BaseTool -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.permissions import Rule, Ruleset from app.services.user_tool_allowlist import TrustedToolSaver diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/bundle.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/bundle.py index 111244784..5940135a8 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/bundle.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/bundle.py @@ -10,7 +10,7 @@ from langchain.agents.middleware import ( ToolCallLimitMiddleware, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import RetryAfterMiddleware from app.agents.new_chat.middleware.scoped_model_fallback import ( ScopedModelFallbackMiddleware, diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/fallback.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/fallback.py index ea68a764e..1146ae887 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/fallback.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/fallback.py @@ -4,7 +4,7 @@ from __future__ import annotations import logging -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware.scoped_model_fallback import ( ScopedModelFallbackMiddleware, ) diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/model_call_limit.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/model_call_limit.py index 85707a385..956870efb 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/model_call_limit.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/model_call_limit.py @@ -4,7 +4,7 @@ from __future__ import annotations from langchain.agents.middleware import ModelCallLimitMiddleware -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from ..flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/retry.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/retry.py index c98fc4083..2b08f0cbb 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/retry.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/retry.py @@ -2,7 +2,7 @@ from __future__ import annotations -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware import RetryAfterMiddleware from ..flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/tool_call_limit.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/tool_call_limit.py index dcde81f37..08bae40bd 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/tool_call_limit.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/shared/resilience/tool_call_limit.py @@ -4,7 +4,7 @@ from __future__ import annotations from langchain.agents.middleware import ToolCallLimitMiddleware -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from ..flags import enabled diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/stack.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/stack.py index 3b20d8915..51e00e954 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/stack.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/stack.py @@ -31,7 +31,7 @@ from app.agents.multi_agent_chat.subagents.builtins.knowledge_base.agent import from app.agents.multi_agent_chat.subagents.builtins.knowledge_base.ask_knowledge_base_tool import ( build_ask_knowledge_base_tool, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.filesystem_selection import FilesystemMode from app.db import ChatVisibility diff --git a/surfsense_backend/app/agents/multi_agent_chat/middleware/subagent/middleware_stack.py b/surfsense_backend/app/agents/multi_agent_chat/middleware/subagent/middleware_stack.py index aa6211fcc..5ffc2bcd1 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/middleware/subagent/middleware_stack.py +++ b/surfsense_backend/app/agents/multi_agent_chat/middleware/subagent/middleware_stack.py @@ -14,7 +14,7 @@ from __future__ import annotations from typing import Any -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from ..shared.permissions import build_permission_mw from ..shared.resilience import ResilienceMiddlewares diff --git a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/middleware_stack.py b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/middleware_stack.py index 778bb250c..c6cfc212c 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/middleware_stack.py +++ b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/middleware_stack.py @@ -28,7 +28,7 @@ from app.agents.multi_agent_chat.middleware.shared.patch_tool_calls import ( from app.agents.multi_agent_chat.middleware.shared.permissions import ( build_permission_mw, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.filesystem_selection import FilesystemMode from app.agents.new_chat.permissions import Ruleset diff --git a/surfsense_backend/app/agents/new_chat/feature_flags.py b/surfsense_backend/app/agents/new_chat/feature_flags.py index 27188fac3..43e671952 100644 --- a/surfsense_backend/app/agents/new_chat/feature_flags.py +++ b/surfsense_backend/app/agents/new_chat/feature_flags.py @@ -1,254 +1,19 @@ -""" -Feature flags for the SurfSense new_chat agent stack. +"""Backward-compatible shim. -These flags gate the newer agent middleware (some ported from OpenCode, -some sourced from ``langchain.agents.middleware`` / ``deepagents``, some -SurfSense-native). Most shipped agent-stack upgrades default ON so Docker -image updates work even when older installs do not have newly introduced -environment variables. Risky/experimental integrations stay default OFF, -and the master kill-switch can still disable everything new. - -All new middleware checks its flag at agent build time. If the master -kill-switch ``SURFSENSE_DISABLE_NEW_AGENT_STACK`` is set, every new -middleware is disabled regardless of its individual flag. This gives -operators a single switch to revert to pre-port behavior. - -Examples --------- - -Defaults: - - SURFSENSE_ENABLE_CONTEXT_EDITING=true - SURFSENSE_ENABLE_COMPACTION_V2=true - SURFSENSE_ENABLE_RETRY_AFTER=true - SURFSENSE_ENABLE_MODEL_FALLBACK=false - SURFSENSE_ENABLE_MODEL_CALL_LIMIT=true - SURFSENSE_ENABLE_TOOL_CALL_LIMIT=true - SURFSENSE_ENABLE_TOOL_CALL_REPAIR=true - SURFSENSE_ENABLE_PERMISSION=true - SURFSENSE_ENABLE_DOOM_LOOP=true - SURFSENSE_ENABLE_LLM_TOOL_SELECTOR=false # adds a per-turn LLM call - -Master kill-switch (overrides everything else): - - SURFSENSE_DISABLE_NEW_AGENT_STACK=true +The agent feature-flag resolver moved to :mod:`app.agents.shared.feature_flags` +as part of promoting the shared agent toolkit out of ``new_chat`` into the +cross-agent kernel. Import from there directly; this re-export keeps the +not-yet-retired single-agent stack working during the migration and will be +removed with it. """ from __future__ import annotations -import logging -import os -from dataclasses import dataclass - -logger = logging.getLogger(__name__) - - -def _env_bool(name: str, default: bool) -> bool: - """Parse a boolean env var. Accepts ``1``/``true``/``yes``/``on`` (case-insensitive).""" - raw = os.environ.get(name) - if raw is None: - return default - return raw.strip().lower() in ("1", "true", "yes", "on") - - -@dataclass(frozen=True) -class AgentFeatureFlags: - """Resolved feature-flag state for one agent build. - - Constructed via :meth:`from_env`. The dataclass is frozen so it can be - safely shared across coroutines. - """ - - # Master kill-switch — when true, every flag below resolves to False - # regardless of its env value. Used for rapid rollback. - disable_new_agent_stack: bool = False - - # Agent quality — context budget, retry/limits, name-repair, doom-loop - enable_context_editing: bool = True - enable_compaction_v2: bool = True - enable_retry_after: bool = True - enable_model_fallback: bool = False - enable_model_call_limit: bool = True - enable_tool_call_limit: bool = True - enable_tool_call_repair: bool = True - enable_doom_loop: bool = True - - # Safety — permissions, concurrency, tool-set narrowing - enable_permission: bool = True - enable_busy_mutex: bool = True - enable_llm_tool_selector: bool = False # Default OFF — adds per-turn LLM cost - - # Skills + subagents - enable_skills: bool = True - enable_specialized_subagents: bool = True - enable_kb_planner_runnable: bool = True - - # Snapshot / revert - enable_action_log: bool = True - enable_revert_route: bool = True - - # Plugins - enable_plugin_loader: bool = False - - # Observability — OTel (orthogonal; also requires OTEL_EXPORTER_OTLP_ENDPOINT) - enable_otel: bool = False - - # Performance — compiled-agent cache (Phase 1 + Phase 2). - # When ON, ``create_surfsense_deep_agent`` reuses a previously-compiled - # graph if the cache key matches (LLM config + thread + tool surface + - # flags + system prompt + filesystem mode). Cuts per-turn agent-build - # wall clock from ~4-5s to <50µs on cache hits. - # - # SAFETY (Phase 2 unblocked this default-on): - # All connector mutation tools (``tools/notion``, ``tools/gmail``, - # ``tools/google_drive``, ``tools/dropbox``, ``tools/onedrive``, - # ``tools/google_calendar``, ``tools/confluence``, ``tools/discord``, - # ``tools/teams``, ``tools/luma``, ``connected_accounts``, - # ``update_memory``) now acquire fresh - # short-lived ``AsyncSession`` instances per call via - # :data:`async_session_maker`. The factory still accepts ``db_session`` - # for registry compatibility but ``del``'s it immediately — see any - # of those files' factory docstrings for the rationale. The ``llm`` - # closure is per-(provider, model, config_id) which is already in - # the cache key, so the LLM is safe to share across cached hits of - # the same key. The KB priority middleware reads - # ``mentioned_document_ids`` from ``runtime.context`` (Phase 1.5), - # not its constructor closure, so the same compiled agent serves - # turns with different mention lists correctly. - # - # Rollback: set ``SURFSENSE_ENABLE_AGENT_CACHE=false`` in the - # environment if a regression surfaces. The path is exercised by - # the ``tests/unit/agents/new_chat/test_agent_cache_*`` suite. - enable_agent_cache: bool = True - # Phase 1 (deferred — measure first): pre-build & share the - # general-purpose subagent ``CompiledSubAgent`` across cold-cache - # misses. Only helps when the outer cache MISSES (cache hits already - # reuse the entire SubAgentMiddleware-compiled graph). Off by default - # until we have data showing cold misses are frequent enough to - # justify the extra global state. - enable_agent_cache_share_gp_subagent: bool = False - - @classmethod - def from_env(cls) -> AgentFeatureFlags: - """Read flags from environment. - - Master kill-switch is evaluated first; when set, all other flags - force to False. - """ - master_off = _env_bool("SURFSENSE_DISABLE_NEW_AGENT_STACK", False) - if master_off: - logger.info( - "SURFSENSE_DISABLE_NEW_AGENT_STACK is set: every new agent " - "middleware is forced OFF for this build." - ) - return cls( - disable_new_agent_stack=True, - enable_context_editing=False, - enable_compaction_v2=False, - enable_retry_after=False, - enable_model_fallback=False, - enable_model_call_limit=False, - enable_tool_call_limit=False, - enable_tool_call_repair=False, - enable_doom_loop=False, - enable_permission=False, - enable_busy_mutex=False, - enable_llm_tool_selector=False, - enable_skills=False, - enable_specialized_subagents=False, - enable_kb_planner_runnable=False, - enable_action_log=False, - enable_revert_route=False, - enable_plugin_loader=False, - enable_otel=False, - enable_agent_cache=False, - enable_agent_cache_share_gp_subagent=False, - ) - - return cls( - disable_new_agent_stack=False, - # Agent quality - enable_context_editing=_env_bool("SURFSENSE_ENABLE_CONTEXT_EDITING", True), - enable_compaction_v2=_env_bool("SURFSENSE_ENABLE_COMPACTION_V2", True), - enable_retry_after=_env_bool("SURFSENSE_ENABLE_RETRY_AFTER", True), - enable_model_fallback=_env_bool("SURFSENSE_ENABLE_MODEL_FALLBACK", False), - enable_model_call_limit=_env_bool( - "SURFSENSE_ENABLE_MODEL_CALL_LIMIT", True - ), - enable_tool_call_limit=_env_bool("SURFSENSE_ENABLE_TOOL_CALL_LIMIT", True), - enable_tool_call_repair=_env_bool( - "SURFSENSE_ENABLE_TOOL_CALL_REPAIR", True - ), - enable_doom_loop=_env_bool("SURFSENSE_ENABLE_DOOM_LOOP", True), - # Safety - enable_permission=_env_bool("SURFSENSE_ENABLE_PERMISSION", True), - enable_busy_mutex=_env_bool("SURFSENSE_ENABLE_BUSY_MUTEX", True), - enable_llm_tool_selector=_env_bool( - "SURFSENSE_ENABLE_LLM_TOOL_SELECTOR", False - ), - # Skills + subagents - enable_skills=_env_bool("SURFSENSE_ENABLE_SKILLS", True), - enable_specialized_subagents=_env_bool( - "SURFSENSE_ENABLE_SPECIALIZED_SUBAGENTS", True - ), - enable_kb_planner_runnable=_env_bool( - "SURFSENSE_ENABLE_KB_PLANNER_RUNNABLE", True - ), - # Snapshot / revert - enable_action_log=_env_bool("SURFSENSE_ENABLE_ACTION_LOG", True), - enable_revert_route=_env_bool("SURFSENSE_ENABLE_REVERT_ROUTE", True), - # Plugins - enable_plugin_loader=_env_bool("SURFSENSE_ENABLE_PLUGIN_LOADER", False), - # Observability - enable_otel=_env_bool("SURFSENSE_ENABLE_OTEL", False), - # Performance - enable_agent_cache=_env_bool("SURFSENSE_ENABLE_AGENT_CACHE", True), - enable_agent_cache_share_gp_subagent=_env_bool( - "SURFSENSE_ENABLE_AGENT_CACHE_SHARE_GP_SUBAGENT", False - ), - ) - - def any_new_middleware_enabled(self) -> bool: - """Return True if any new middleware flag is on.""" - if self.disable_new_agent_stack: - return False - return any( - ( - self.enable_context_editing, - self.enable_compaction_v2, - self.enable_retry_after, - self.enable_model_fallback, - self.enable_model_call_limit, - self.enable_tool_call_limit, - self.enable_tool_call_repair, - self.enable_doom_loop, - self.enable_permission, - self.enable_busy_mutex, - self.enable_llm_tool_selector, - self.enable_skills, - self.enable_specialized_subagents, - self.enable_kb_planner_runnable, - self.enable_action_log, - self.enable_revert_route, - self.enable_plugin_loader, - ) - ) - - -def get_flags() -> AgentFeatureFlags: - """Return the resolved feature-flag state from the **current** process environment. - - Intentionally **not** cached: ``load_dotenv`` and operator edits to env vars - must affect the next agent build without requiring a full process restart. - Cost is negligible (reads ``os.environ`` once per call). - """ - return AgentFeatureFlags.from_env() - - -def reload_for_tests() -> AgentFeatureFlags: - """Compatibility helper for tests; equivalent to :func:`get_flags`.""" - return AgentFeatureFlags.from_env() - +from app.agents.shared.feature_flags import ( + AgentFeatureFlags, + get_flags, + reload_for_tests, +) __all__ = [ "AgentFeatureFlags", diff --git a/surfsense_backend/app/agents/new_chat/middleware/action_log.py b/surfsense_backend/app/agents/new_chat/middleware/action_log.py index 716a1616c..ff6c9c53e 100644 --- a/surfsense_backend/app/agents/new_chat/middleware/action_log.py +++ b/surfsense_backend/app/agents/new_chat/middleware/action_log.py @@ -33,7 +33,7 @@ from langchain.agents.middleware import AgentMiddleware from langchain_core.callbacks import adispatch_custom_event from langchain_core.messages import ToolMessage -from app.agents.new_chat.feature_flags import get_flags +from app.agents.shared.feature_flags import get_flags from app.agents.new_chat.tools.registry import ToolDefinition if TYPE_CHECKING: # pragma: no cover - type-only diff --git a/surfsense_backend/app/agents/new_chat/middleware/kb_persistence.py b/surfsense_backend/app/agents/new_chat/middleware/kb_persistence.py index 88d89b287..5e45882de 100644 --- a/surfsense_backend/app/agents/new_chat/middleware/kb_persistence.py +++ b/surfsense_backend/app/agents/new_chat/middleware/kb_persistence.py @@ -45,7 +45,7 @@ from sqlalchemy import delete, select, update from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.new_chat.feature_flags import get_flags +from app.agents.shared.feature_flags import get_flags from app.agents.new_chat.filesystem_selection import FilesystemMode from app.agents.new_chat.filesystem_state import SurfSenseFilesystemState from app.agents.new_chat.path_resolver import ( diff --git a/surfsense_backend/app/agents/new_chat/middleware/knowledge_search.py b/surfsense_backend/app/agents/new_chat/middleware/knowledge_search.py index 77b413940..967e8247b 100644 --- a/surfsense_backend/app/agents/new_chat/middleware/knowledge_search.py +++ b/surfsense_backend/app/agents/new_chat/middleware/knowledge_search.py @@ -41,7 +41,7 @@ from litellm import token_counter from pydantic import BaseModel, Field, ValidationError from sqlalchemy import select -from app.agents.new_chat.feature_flags import get_flags +from app.agents.shared.feature_flags import get_flags from app.agents.new_chat.filesystem_selection import FilesystemMode from app.agents.new_chat.filesystem_state import SurfSenseFilesystemState from app.agents.new_chat.path_resolver import ( diff --git a/surfsense_backend/app/agents/shared/feature_flags.py b/surfsense_backend/app/agents/shared/feature_flags.py new file mode 100644 index 000000000..27188fac3 --- /dev/null +++ b/surfsense_backend/app/agents/shared/feature_flags.py @@ -0,0 +1,257 @@ +""" +Feature flags for the SurfSense new_chat agent stack. + +These flags gate the newer agent middleware (some ported from OpenCode, +some sourced from ``langchain.agents.middleware`` / ``deepagents``, some +SurfSense-native). Most shipped agent-stack upgrades default ON so Docker +image updates work even when older installs do not have newly introduced +environment variables. Risky/experimental integrations stay default OFF, +and the master kill-switch can still disable everything new. + +All new middleware checks its flag at agent build time. If the master +kill-switch ``SURFSENSE_DISABLE_NEW_AGENT_STACK`` is set, every new +middleware is disabled regardless of its individual flag. This gives +operators a single switch to revert to pre-port behavior. + +Examples +-------- + +Defaults: + + SURFSENSE_ENABLE_CONTEXT_EDITING=true + SURFSENSE_ENABLE_COMPACTION_V2=true + SURFSENSE_ENABLE_RETRY_AFTER=true + SURFSENSE_ENABLE_MODEL_FALLBACK=false + SURFSENSE_ENABLE_MODEL_CALL_LIMIT=true + SURFSENSE_ENABLE_TOOL_CALL_LIMIT=true + SURFSENSE_ENABLE_TOOL_CALL_REPAIR=true + SURFSENSE_ENABLE_PERMISSION=true + SURFSENSE_ENABLE_DOOM_LOOP=true + SURFSENSE_ENABLE_LLM_TOOL_SELECTOR=false # adds a per-turn LLM call + +Master kill-switch (overrides everything else): + + SURFSENSE_DISABLE_NEW_AGENT_STACK=true +""" + +from __future__ import annotations + +import logging +import os +from dataclasses import dataclass + +logger = logging.getLogger(__name__) + + +def _env_bool(name: str, default: bool) -> bool: + """Parse a boolean env var. Accepts ``1``/``true``/``yes``/``on`` (case-insensitive).""" + raw = os.environ.get(name) + if raw is None: + return default + return raw.strip().lower() in ("1", "true", "yes", "on") + + +@dataclass(frozen=True) +class AgentFeatureFlags: + """Resolved feature-flag state for one agent build. + + Constructed via :meth:`from_env`. The dataclass is frozen so it can be + safely shared across coroutines. + """ + + # Master kill-switch — when true, every flag below resolves to False + # regardless of its env value. Used for rapid rollback. + disable_new_agent_stack: bool = False + + # Agent quality — context budget, retry/limits, name-repair, doom-loop + enable_context_editing: bool = True + enable_compaction_v2: bool = True + enable_retry_after: bool = True + enable_model_fallback: bool = False + enable_model_call_limit: bool = True + enable_tool_call_limit: bool = True + enable_tool_call_repair: bool = True + enable_doom_loop: bool = True + + # Safety — permissions, concurrency, tool-set narrowing + enable_permission: bool = True + enable_busy_mutex: bool = True + enable_llm_tool_selector: bool = False # Default OFF — adds per-turn LLM cost + + # Skills + subagents + enable_skills: bool = True + enable_specialized_subagents: bool = True + enable_kb_planner_runnable: bool = True + + # Snapshot / revert + enable_action_log: bool = True + enable_revert_route: bool = True + + # Plugins + enable_plugin_loader: bool = False + + # Observability — OTel (orthogonal; also requires OTEL_EXPORTER_OTLP_ENDPOINT) + enable_otel: bool = False + + # Performance — compiled-agent cache (Phase 1 + Phase 2). + # When ON, ``create_surfsense_deep_agent`` reuses a previously-compiled + # graph if the cache key matches (LLM config + thread + tool surface + + # flags + system prompt + filesystem mode). Cuts per-turn agent-build + # wall clock from ~4-5s to <50µs on cache hits. + # + # SAFETY (Phase 2 unblocked this default-on): + # All connector mutation tools (``tools/notion``, ``tools/gmail``, + # ``tools/google_drive``, ``tools/dropbox``, ``tools/onedrive``, + # ``tools/google_calendar``, ``tools/confluence``, ``tools/discord``, + # ``tools/teams``, ``tools/luma``, ``connected_accounts``, + # ``update_memory``) now acquire fresh + # short-lived ``AsyncSession`` instances per call via + # :data:`async_session_maker`. The factory still accepts ``db_session`` + # for registry compatibility but ``del``'s it immediately — see any + # of those files' factory docstrings for the rationale. The ``llm`` + # closure is per-(provider, model, config_id) which is already in + # the cache key, so the LLM is safe to share across cached hits of + # the same key. The KB priority middleware reads + # ``mentioned_document_ids`` from ``runtime.context`` (Phase 1.5), + # not its constructor closure, so the same compiled agent serves + # turns with different mention lists correctly. + # + # Rollback: set ``SURFSENSE_ENABLE_AGENT_CACHE=false`` in the + # environment if a regression surfaces. The path is exercised by + # the ``tests/unit/agents/new_chat/test_agent_cache_*`` suite. + enable_agent_cache: bool = True + # Phase 1 (deferred — measure first): pre-build & share the + # general-purpose subagent ``CompiledSubAgent`` across cold-cache + # misses. Only helps when the outer cache MISSES (cache hits already + # reuse the entire SubAgentMiddleware-compiled graph). Off by default + # until we have data showing cold misses are frequent enough to + # justify the extra global state. + enable_agent_cache_share_gp_subagent: bool = False + + @classmethod + def from_env(cls) -> AgentFeatureFlags: + """Read flags from environment. + + Master kill-switch is evaluated first; when set, all other flags + force to False. + """ + master_off = _env_bool("SURFSENSE_DISABLE_NEW_AGENT_STACK", False) + if master_off: + logger.info( + "SURFSENSE_DISABLE_NEW_AGENT_STACK is set: every new agent " + "middleware is forced OFF for this build." + ) + return cls( + disable_new_agent_stack=True, + enable_context_editing=False, + enable_compaction_v2=False, + enable_retry_after=False, + enable_model_fallback=False, + enable_model_call_limit=False, + enable_tool_call_limit=False, + enable_tool_call_repair=False, + enable_doom_loop=False, + enable_permission=False, + enable_busy_mutex=False, + enable_llm_tool_selector=False, + enable_skills=False, + enable_specialized_subagents=False, + enable_kb_planner_runnable=False, + enable_action_log=False, + enable_revert_route=False, + enable_plugin_loader=False, + enable_otel=False, + enable_agent_cache=False, + enable_agent_cache_share_gp_subagent=False, + ) + + return cls( + disable_new_agent_stack=False, + # Agent quality + enable_context_editing=_env_bool("SURFSENSE_ENABLE_CONTEXT_EDITING", True), + enable_compaction_v2=_env_bool("SURFSENSE_ENABLE_COMPACTION_V2", True), + enable_retry_after=_env_bool("SURFSENSE_ENABLE_RETRY_AFTER", True), + enable_model_fallback=_env_bool("SURFSENSE_ENABLE_MODEL_FALLBACK", False), + enable_model_call_limit=_env_bool( + "SURFSENSE_ENABLE_MODEL_CALL_LIMIT", True + ), + enable_tool_call_limit=_env_bool("SURFSENSE_ENABLE_TOOL_CALL_LIMIT", True), + enable_tool_call_repair=_env_bool( + "SURFSENSE_ENABLE_TOOL_CALL_REPAIR", True + ), + enable_doom_loop=_env_bool("SURFSENSE_ENABLE_DOOM_LOOP", True), + # Safety + enable_permission=_env_bool("SURFSENSE_ENABLE_PERMISSION", True), + enable_busy_mutex=_env_bool("SURFSENSE_ENABLE_BUSY_MUTEX", True), + enable_llm_tool_selector=_env_bool( + "SURFSENSE_ENABLE_LLM_TOOL_SELECTOR", False + ), + # Skills + subagents + enable_skills=_env_bool("SURFSENSE_ENABLE_SKILLS", True), + enable_specialized_subagents=_env_bool( + "SURFSENSE_ENABLE_SPECIALIZED_SUBAGENTS", True + ), + enable_kb_planner_runnable=_env_bool( + "SURFSENSE_ENABLE_KB_PLANNER_RUNNABLE", True + ), + # Snapshot / revert + enable_action_log=_env_bool("SURFSENSE_ENABLE_ACTION_LOG", True), + enable_revert_route=_env_bool("SURFSENSE_ENABLE_REVERT_ROUTE", True), + # Plugins + enable_plugin_loader=_env_bool("SURFSENSE_ENABLE_PLUGIN_LOADER", False), + # Observability + enable_otel=_env_bool("SURFSENSE_ENABLE_OTEL", False), + # Performance + enable_agent_cache=_env_bool("SURFSENSE_ENABLE_AGENT_CACHE", True), + enable_agent_cache_share_gp_subagent=_env_bool( + "SURFSENSE_ENABLE_AGENT_CACHE_SHARE_GP_SUBAGENT", False + ), + ) + + def any_new_middleware_enabled(self) -> bool: + """Return True if any new middleware flag is on.""" + if self.disable_new_agent_stack: + return False + return any( + ( + self.enable_context_editing, + self.enable_compaction_v2, + self.enable_retry_after, + self.enable_model_fallback, + self.enable_model_call_limit, + self.enable_tool_call_limit, + self.enable_tool_call_repair, + self.enable_doom_loop, + self.enable_permission, + self.enable_busy_mutex, + self.enable_llm_tool_selector, + self.enable_skills, + self.enable_specialized_subagents, + self.enable_kb_planner_runnable, + self.enable_action_log, + self.enable_revert_route, + self.enable_plugin_loader, + ) + ) + + +def get_flags() -> AgentFeatureFlags: + """Return the resolved feature-flag state from the **current** process environment. + + Intentionally **not** cached: ``load_dotenv`` and operator edits to env vars + must affect the next agent build without requiring a full process restart. + Cost is negligible (reads ``os.environ`` once per call). + """ + return AgentFeatureFlags.from_env() + + +def reload_for_tests() -> AgentFeatureFlags: + """Compatibility helper for tests; equivalent to :func:`get_flags`.""" + return AgentFeatureFlags.from_env() + + +__all__ = [ + "AgentFeatureFlags", + "get_flags", + "reload_for_tests", +] diff --git a/surfsense_backend/app/routes/agent_action_log_route.py b/surfsense_backend/app/routes/agent_action_log_route.py index 2608aa3b1..519424ba7 100644 --- a/surfsense_backend/app/routes/agent_action_log_route.py +++ b/surfsense_backend/app/routes/agent_action_log_route.py @@ -28,7 +28,7 @@ from pydantic import BaseModel from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.new_chat.feature_flags import get_flags +from app.agents.shared.feature_flags import get_flags from app.db import ( AgentActionLog, NewChatThread, diff --git a/surfsense_backend/app/routes/agent_flags_route.py b/surfsense_backend/app/routes/agent_flags_route.py index 99388af66..d23650349 100644 --- a/surfsense_backend/app/routes/agent_flags_route.py +++ b/surfsense_backend/app/routes/agent_flags_route.py @@ -22,7 +22,7 @@ from dataclasses import asdict from fastapi import APIRouter, Depends from pydantic import BaseModel -from app.agents.new_chat.feature_flags import AgentFeatureFlags, get_flags +from app.agents.shared.feature_flags import AgentFeatureFlags, get_flags from app.config import config from app.db import User from app.users import current_active_user diff --git a/surfsense_backend/app/routes/agent_permissions_route.py b/surfsense_backend/app/routes/agent_permissions_route.py index 1c76e00e6..6151bb871 100644 --- a/surfsense_backend/app/routes/agent_permissions_route.py +++ b/surfsense_backend/app/routes/agent_permissions_route.py @@ -30,7 +30,7 @@ from sqlalchemy import select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.new_chat.feature_flags import get_flags +from app.agents.shared.feature_flags import get_flags from app.db import ( AgentPermissionRule, NewChatThread, diff --git a/surfsense_backend/app/routes/agent_revert_route.py b/surfsense_backend/app/routes/agent_revert_route.py index 711081b15..230304087 100644 --- a/surfsense_backend/app/routes/agent_revert_route.py +++ b/surfsense_backend/app/routes/agent_revert_route.py @@ -32,7 +32,7 @@ from sqlalchemy import select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import AsyncSession -from app.agents.new_chat.feature_flags import get_flags +from app.agents.shared.feature_flags import get_flags from app.db import ( AgentActionLog, User, diff --git a/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_permission_ask_mcp_context.py b/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_permission_ask_mcp_context.py index c9bd4e142..1eaac5113 100644 --- a/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_permission_ask_mcp_context.py +++ b/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_permission_ask_mcp_context.py @@ -19,7 +19,7 @@ from app.agents.multi_agent_chat.middleware.shared.permissions import ( from app.agents.multi_agent_chat.middleware.shared.permissions.ask.payload import ( build_permission_ask_payload, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.permissions import Rule, Ruleset diff --git a/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_subagent_owned_ruleset.py b/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_subagent_owned_ruleset.py index b9ac6cd15..66dc5d76f 100644 --- a/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_subagent_owned_ruleset.py +++ b/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_subagent_owned_ruleset.py @@ -26,7 +26,7 @@ from typing_extensions import TypedDict from app.agents.multi_agent_chat.middleware.shared.permissions import ( build_permission_mw, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.permissions import Rule, Ruleset diff --git a/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_trusted_tool_save_on_always.py b/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_trusted_tool_save_on_always.py index 47d3704ac..e3493b9bb 100644 --- a/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_trusted_tool_save_on_always.py +++ b/surfsense_backend/tests/unit/agents/multi_agent_chat/middleware/shared/permissions/test_trusted_tool_save_on_always.py @@ -17,7 +17,7 @@ from typing_extensions import TypedDict from app.agents.multi_agent_chat.middleware.shared.permissions import ( build_permission_mw, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.permissions import Rule, Ruleset diff --git a/surfsense_backend/tests/unit/agents/multi_agent_chat/subagents/shared/test_subagent_builder.py b/surfsense_backend/tests/unit/agents/multi_agent_chat/subagents/shared/test_subagent_builder.py index 062ea92ec..e65cffe47 100644 --- a/surfsense_backend/tests/unit/agents/multi_agent_chat/subagents/shared/test_subagent_builder.py +++ b/surfsense_backend/tests/unit/agents/multi_agent_chat/subagents/shared/test_subagent_builder.py @@ -25,7 +25,7 @@ from app.agents.multi_agent_chat.middleware.shared.permissions.middleware.core i from app.agents.multi_agent_chat.subagents.shared.subagent_builder import ( pack_subagent, ) -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.permissions import Rule, Ruleset, evaluate diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_action_log.py b/surfsense_backend/tests/unit/agents/new_chat/test_action_log.py index 8ef1430a9..7772a38d4 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_action_log.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_action_log.py @@ -10,7 +10,7 @@ import pytest from langchain_core.messages import ToolMessage from langchain_core.tools import tool -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.agents.new_chat.middleware.action_log import ActionLogMiddleware from app.agents.new_chat.tools.registry import ToolDefinition diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_feature_flags.py b/surfsense_backend/tests/unit/agents/new_chat/test_feature_flags.py index 099aea882..404831f06 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_feature_flags.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_feature_flags.py @@ -4,7 +4,7 @@ from __future__ import annotations import pytest -from app.agents.new_chat.feature_flags import ( +from app.agents.shared.feature_flags import ( AgentFeatureFlags, reload_for_tests, ) diff --git a/surfsense_backend/tests/unit/routes/test_revert_turn_route.py b/surfsense_backend/tests/unit/routes/test_revert_turn_route.py index 1e1cbffb3..35ba2b088 100644 --- a/surfsense_backend/tests/unit/routes/test_revert_turn_route.py +++ b/surfsense_backend/tests/unit/routes/test_revert_turn_route.py @@ -18,7 +18,7 @@ from unittest.mock import AsyncMock, patch import pytest -from app.agents.new_chat.feature_flags import AgentFeatureFlags +from app.agents.shared.feature_flags import AgentFeatureFlags from app.routes import agent_revert_route from app.services.revert_service import RevertOutcome