Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
d6e605f
feat(notion-mcp): add OAuth + PKCE service layer and MCP adapter
CREDO23 Apr 20, 2026
41d5479
feat(notion-mcp): add MCP connector OAuth routes
CREDO23 Apr 20, 2026
8d438f5
feat(notion-mcp): add MCP agent tool factories and registry wiring
CREDO23 Apr 20, 2026
5a8ec70
feat(notion-mcp): add MCP gating in agent, indexer, and health check
CREDO23 Apr 20, 2026
30944c0
feat(notion-mcp): wire frontend to MCP OAuth endpoints
CREDO23 Apr 20, 2026
7f0a5cd
fix(hitl-edit-panel): move duplicate-tag check into functional setTags
mvanhorn Apr 21, 2026
2657c9e
feat: add internal backend URL configuration
AnishSarkar22 Apr 21, 2026
59f8696
feat: add extra_hosts configuration for Docker services
AnishSarkar22 Apr 21, 2026
7c23f31
feat: add Ollama setup guide to documentation
AnishSarkar22 Apr 21, 2026
c1754e4
Merge pull request #1288 from MODSetter/dev
MODSetter Apr 21, 2026
41ff156
Merge pull request #1281 from mvanhorn/osc/1248-handle-add-tag-functi…
MODSetter Apr 21, 2026
875c4c3
add connector exception hierarchy
CREDO23 Apr 21, 2026
45acf9d
add async retry utility with tenacity
CREDO23 Apr 21, 2026
474c35f
add standardized tool response helper
CREDO23 Apr 21, 2026
6529889
add declarative connector gating to tool registry
CREDO23 Apr 21, 2026
a180426
replace manual connector checks with declarative gating
CREDO23 Apr 21, 2026
2dfe03b
add reusable OAuth connector route base class
CREDO23 Apr 21, 2026
ea3bda9
delete Notion MCP services, tools, and route
CREDO23 Apr 21, 2026
291c107
remove Notion MCP router from routes
CREDO23 Apr 21, 2026
4815874
remove Notion MCP tool definitions from registry
CREDO23 Apr 21, 2026
177a346
remove Notion MCP gating from agent
CREDO23 Apr 21, 2026
978a8e2
remove MCP health check branch from Notion metadata service
CREDO23 Apr 21, 2026
e02fbbe
remove MCP-mode skip from Notion indexer
CREDO23 Apr 21, 2026
c70f0cc
revert Notion auth URLs to classic OAuth endpoints
CREDO23 Apr 21, 2026
d2cb778
add Gmail search and read email tools
CREDO23 Apr 21, 2026
07a5fac
add Calendar search events tool
CREDO23 Apr 21, 2026
1de2517
add Discord list channels, read messages, send message tools
CREDO23 Apr 21, 2026
49f8d1a
add Teams list channels, read messages, send message tools
CREDO23 Apr 21, 2026
ba8e313
add Luma list events, read event, create event tools
CREDO23 Apr 21, 2026
575b2c6
register all new live connector tools in registry
CREDO23 Apr 21, 2026
7133655
add MCP service registry for Linear, Jira, ClickUp
CREDO23 Apr 21, 2026
4efdee5
add MCP OAuth discovery, DCR, and token exchange
CREDO23 Apr 21, 2026
45867e5
add generic MCP OAuth route with DCR + PKCE
CREDO23 Apr 21, 2026
81711c9
wire MCP OAuth route into app router
CREDO23 Apr 21, 2026
9b78fbf
add automatic token refresh for MCP OAuth connectors
CREDO23 Apr 21, 2026
c414cc2
add frontend tiles for Linear, Jira, ClickUp MCP connectors
CREDO23 Apr 21, 2026
8b8c9b1
add Slack and Airtable MCP OAuth support
CREDO23 Apr 21, 2026
5ff0ec5
disable periodic indexing for live connectors
CREDO23 Apr 21, 2026
328219e
disable first-run indexing for live connectors
CREDO23 Apr 21, 2026
53a173a
guard manual indexing for live connectors
CREDO23 Apr 21, 2026
0ab7d6a
set is_indexable=False for all live connector add routes
CREDO23 Apr 21, 2026
e676ebf
remove live connectors from AUTO_INDEX_DEFAULTS
CREDO23 Apr 21, 2026
5353d45
Merge pull request #1287 from AnishSarkar22/fix/docker-host-gateway
MODSetter Apr 22, 2026
4356a24
Merge pull request #1289 from MODSetter/dev
MODSetter Apr 22, 2026
bd2d6c0
fix: summarization middleware usage
MODSetter Apr 22, 2026
940889c
fix open redirect, error leaking, unused imports, state validation
CREDO23 Apr 22, 2026
ea3508c
use native connector types for MCP OAuth, restore original UI
CREDO23 Apr 22, 2026
c277b6c
skip indexing config dialog for non-indexable connectors
CREDO23 Apr 22, 2026
2f4052a
use pre-configured credentials for Airtable MCP OAuth
CREDO23 Apr 22, 2026
0cc2475
add required OAuth scopes for Airtable MCP
CREDO23 Apr 22, 2026
225236e
add required OAuth scopes for Slack MCP
CREDO23 Apr 22, 2026
3638d72
restore full Slack MCP scopes for all MCP tools
CREDO23 Apr 22, 2026
820326e
use user_scope param for Slack OAuth
CREDO23 Apr 22, 2026
95de054
feat: add default API base URLs for LiteLLM providers and enhance fal…
MODSetter Apr 22, 2026
970f622
revert scope_param, use standard scope for Slack v2_user endpoint
CREDO23 Apr 22, 2026
9b56d45
Merge commit '4356a2499b01cadf8767cd6bc03d61e80356793b' into dev
MODSetter Apr 22, 2026
c85c3a4
Merge pull request #1290 from MODSetter/dev_mod
MODSetter Apr 22, 2026
80d3f62
chore: linting
MODSetter Apr 22, 2026
dde1948
fix Slack MCP OAuth: v2 endpoint, user_scope param, nested token extr…
CREDO23 Apr 22, 2026
f02909e
Merge pull request #1291 from MODSetter/dev
MODSetter Apr 22, 2026
dfa40b8
fix MCP OAuth for all 5 services, add MCP connector edit view
CREDO23 Apr 22, 2026
a4bc621
uniform connector UX across all connector types
CREDO23 Apr 22, 2026
435406c
temp(hotpatch): block github_coplot custom provider
MODSetter Apr 22, 2026
b067c92
Merge commit 'f02909e378f6e7b1a72842c224012de35967fdfd' into dev
MODSetter Apr 22, 2026
144e513
feat: implement resume page limit functionality
AnishSarkar22 Apr 22, 2026
8b67d54
docs: update resume template guidelines for skills sections
AnishSarkar22 Apr 22, 2026
8ca4465
fix: adjust spacing in resume template for highlights
AnishSarkar22 Apr 22, 2026
9eb54bc
add account metadata discovery and connected_accounts tool
CREDO23 Apr 22, 2026
f2d9e67
add multi-account MCP tool disambiguation with prefix namespacing
CREDO23 Apr 22, 2026
9452d10
replace broken native Jira/Linear tools with MCP equivalents
CREDO23 Apr 22, 2026
a4134f1
fix alembic migration version numbering
CREDO23 Apr 22, 2026
336bd57
refactor: enhance button loading states and styling in editor and sid…
AnishSarkar22 Apr 22, 2026
4915675
Merge upstream/dev into feature/mcp-migration
CREDO23 Apr 22, 2026
a1d03da
fix: encrypt tokens at rest, invalidate cache on refresh, clean up lo…
CREDO23 Apr 22, 2026
01153b0
fix: cache TokenEncryption, clear stale router caches on re-init
CREDO23 Apr 22, 2026
0eae96b
fix: harden MCP OAuth and connector edge cases
CREDO23 Apr 22, 2026
f5376f2
Merge pull request #1293 from AnishSarkar22/fix/resume-page-limit
MODSetter Apr 22, 2026
9977f9b
remove dead indexing tasks and fix silent schedule breakage for live …
CREDO23 Apr 22, 2026
b6c506a
fix: treat all Gmail/Calendar as live connectors, hide indexing UI
CREDO23 Apr 22, 2026
7245ab4
Merge pull request #1294 from CREDO23/feature/mcp-migration
MODSetter Apr 23, 2026
16f4757
Enhance MCP tool trust functionality to support OAuth-backed connecto…
CREDO23 Apr 23, 2026
e3172dc
fix: reactive 401 recovery for live MCP connectors and unified reauth…
CREDO23 Apr 23, 2026
0b3551b
perf: parallelize MCP connector discovery with per-connector timeout
CREDO23 Apr 23, 2026
80a349e
feat: add disconnect functionality for connectors with confirmation p…
CREDO23 Apr 23, 2026
9bb117f
feat: skip edit view for live connectors, disconnect directly from ac…
CREDO23 Apr 23, 2026
2eb0ff9
feat: add reauthentication endpoints for Linear and JIRA connectors
CREDO23 Apr 23, 2026
cf7c14c
fix: mark connector auth_expired on token decryption failure
CREDO23 Apr 23, 2026
1712f45
fix: add spinner loading state to MCP test connection button
CREDO23 Apr 23, 2026
45b72de
fix: robust generic MCP tool routing, retry, and empty-schema handling
CREDO23 Apr 23, 2026
749116e
feat(new-chat): add filesystem backend interfaces and selection helpers
AnishSarkar22 Apr 23, 2026
15a9e8b
feat(middleware): detect file intent in chat messages
AnishSarkar22 Apr 23, 2026
7393456
fix: break circular import in llm_service and kb_sync_service files
CREDO23 Apr 23, 2026
42d2d22
feat(filesystem): add local folder backend and verification coverage
AnishSarkar22 Apr 23, 2026
1eadece
feat(new-chat): integrate filesystem flow into agent pipeline
AnishSarkar22 Apr 23, 2026
5c3a327
feat(desktop): expose agent filesystem IPC APIs
AnishSarkar22 Apr 23, 2026
4899588
feat(web): connect new chat UI to agent filesystem APIs
AnishSarkar22 Apr 23, 2026
a2ddf47
refactor(anon-chat): route upload through anonymousChatApiService
tmchow Apr 23, 2026
864f6f7
feat(filesystem): enhance local file handling in editor and IPC integ…
AnishSarkar22 Apr 23, 2026
bbc1c76
feat(editor): integrate Monaco Editor for local file editing and enha…
AnishSarkar22 Apr 23, 2026
d397fec
feat(editor): add SourceCodeEditor component for enhanced code editin…
AnishSarkar22 Apr 23, 2026
3f203f8
feat(editor): implement auto-save functionality and manual save comma…
AnishSarkar22 Apr 23, 2026
fe9ffa1
refactor(editor): improve SourceCodeEditor styling and enhance scroll…
AnishSarkar22 Apr 23, 2026
9317b3f
refactor(editor): remove auto-save functionality and simplify SourceC…
AnishSarkar22 Apr 23, 2026
06b5092
feat(editor): add mode toggle functionality and improve editor state …
AnishSarkar22 Apr 23, 2026
0381632
refactor(editor): replace Loader2 with Spinner component and enhance …
AnishSarkar22 Apr 23, 2026
a1d3356
feat(editor): add reserveToolbarSpace option to enhance toolbar visib…
AnishSarkar22 Apr 23, 2026
b5921bf
feat(markdown): enhance code block rendering for local web files and …
AnishSarkar22 Apr 23, 2026
18b4a6e
refactor(ui): update icon imports and adjust button styles in editor …
AnishSarkar22 Apr 23, 2026
8414556
feat(editor): implement local filesystem trust dialog and enhance fil…
AnishSarkar22 Apr 23, 2026
fb2aece
feat(settings): add DesktopShortcutsContent component for managing ho…
AnishSarkar22 Apr 23, 2026
09ab174
Merge pull request #1297 from CREDO23/feature/mcp-migration
MODSetter Apr 23, 2026
46056ee
fix(settings): update user settings dialog labels and enhance Desktop…
AnishSarkar22 Apr 23, 2026
daac6b5
feat(login): implement customizable hotkey management in the login pa…
AnishSarkar22 Apr 23, 2026
6721919
feat(filesystem): add multi-root local folder support in backend
AnishSarkar22 Apr 23, 2026
3ee2683
feat(filesystem): propagate localRootPaths across desktop and web API
AnishSarkar22 Apr 23, 2026
a250f97
feat(thread): support selecting and managing multiple local folders
AnishSarkar22 Apr 23, 2026
c1a07a0
refactor(sidebar): use Monitor icon for system theme option
AnishSarkar22 Apr 23, 2026
1e9db6f
feat(filesystem): enhance local mount path normalization and improve …
AnishSarkar22 Apr 23, 2026
17f9ee4
refactor(icons): replace 'Pen' icon with 'Pencil' across various comp…
AnishSarkar22 Apr 23, 2026
2618205
refactor(thread): remove unused filesystem settings and related logic…
AnishSarkar22 Apr 23, 2026
b5400ca
feat(sidebar): implement local filesystem browser and enhance documen…
AnishSarkar22 Apr 23, 2026
d1c1416
feat(sidebar): enhance DocumentsSidebar with dropdown menu for local …
AnishSarkar22 Apr 23, 2026
ce71897
refactor(hotkeys): simplify hotkey display logic and replace icon rep…
AnishSarkar22 Apr 23, 2026
a7a758f
feat(filesystem): add getAgentFilesystemMounts API and integrate with…
AnishSarkar22 Apr 23, 2026
30b55a9
feat(filesystem): refactor local filesystem handling to use mounts in…
AnishSarkar22 Apr 24, 2026
7063d6d
feat(filesystem): improve path normalization in SurfSenseFilesystemMi…
AnishSarkar22 Apr 24, 2026
f3c4daa
Merge pull request #1298 from tmchow/osc/1245-dedupe-anon-upload
MODSetter Apr 24, 2026
a0f2851
Merge pull request #1299 from AnishSarkar22/feat/swappable-filesystem
MODSetter Apr 24, 2026
9b1b9a9
Merge remote-tracking branch 'upstream/dev' into feat/obsidian-plugin
AnishSarkar22 Apr 24, 2026
08740ca
feat: enhance UI components and functionality across documents and re…
AnishSarkar22 Apr 24, 2026
39f56a8
refactor(DocumentsFilters): update input styling and button positioni…
AnishSarkar22 Apr 24, 2026
3b9be79
chore: update SurfSense plugin identifiers and versioning
AnishSarkar22 Apr 24, 2026
e84dc87
feat(obsidian_plugin): validate binary attachments and enforce MIME t…
AnishSarkar22 Apr 24, 2026
1c18735
feat: enhance authentication handling and UI feedback in SurfSense pl…
AnishSarkar22 Apr 24, 2026
68156d2
refactor: centralize status visuals and improve connection indicator …
AnishSarkar22 Apr 24, 2026
4f1c870
feat: update status handling and connection indicator in SurfSense pl…
AnishSarkar22 Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/release-obsidian-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,15 @@ jobs:
fi

# Publish release under bare `manifest.json` version (no `obsidian-v` prefix) for BRAT/store compatibility.
# `make_latest: "false"` keeps the desktop app's `v*` release headlined since Obsidian and BRAT resolve plugins via getReleaseByTag, not the latest flag.
- name: Create GitHub release
if: steps.release_mode.outputs.should_publish == 'true'
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ steps.version.outputs.version }}
name: SurfSense Obsidian Plugin ${{ steps.version.outputs.version }}
generate_release_notes: true
make_latest: "false"
files: |
surfsense_obsidian/main.js
surfsense_obsidian/manifest.json
Expand Down
4 changes: 4 additions & 0 deletions docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ services:
- shared_temp:/shared_tmp
env_file:
- ../surfsense_backend/.env
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- DATABASE_URL=${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}}
- CELERY_BROKER_URL=${REDIS_URL:-redis://redis:6379/0}
Expand Down Expand Up @@ -118,6 +120,8 @@ services:
- shared_temp:/shared_tmp
env_file:
- ../surfsense_backend/.env
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- DATABASE_URL=${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}}
- CELERY_BROKER_URL=${REDIS_URL:-redis://redis:6379/0}
Expand Down
4 changes: 4 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ services:
- shared_temp:/shared_tmp
env_file:
- .env
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-surfsense}:${DB_PASSWORD:-surfsense}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}}
CELERY_BROKER_URL: ${REDIS_URL:-redis://redis:6379/0}
Expand Down Expand Up @@ -100,6 +102,8 @@ services:
- shared_temp:/shared_tmp
env_file:
- .env
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
DATABASE_URL: ${DATABASE_URL:-postgresql+asyncpg://${DB_USER:-surfsense}:${DB_PASSWORD:-surfsense}@${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-surfsense}}
CELERY_BROKER_URL: ${REDIS_URL:-redis://redis:6379/0}
Expand Down
6 changes: 3 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"id": "surfsense",
"id": "surfsense-obsidian",
"name": "SurfSense",
"version": "0.1.1",
"version": "0.1.0",
"minAppVersion": "1.5.4",
"description": "Turn your vault into a searchable second brain with SurfSense.",
"author": "SurfSense",
"authorUrl": "https://github.com/MODSetter/SurfSense",
"authorUrl": "https://www.surfsense.com",
"isDesktopOnly": false
}
3 changes: 3 additions & 0 deletions surfsense_backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,9 @@ LLAMA_CLOUD_API_KEY=llx-nnn
# DAYTONA_TARGET=us
# DAYTONA_SNAPSHOT_ID=

# Desktop local filesystem mode (chat file tools run against a local folder root)
# ENABLE_DESKTOP_LOCAL_FILESYSTEM=FALSE

# OPTIONAL: Add these for LangSmith Observability
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
Expand Down
138 changes: 37 additions & 101 deletions surfsense_backend/app/agents/new_chat/chat_deepagent.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from deepagents.graph import BASE_AGENT_PROMPT
from deepagents.middleware.patch_tool_calls import PatchToolCallsMiddleware
from deepagents.middleware.subagents import GENERAL_PURPOSE_SUBAGENT
from deepagents.middleware.summarization import create_summarization_middleware
from langchain.agents import create_agent
from langchain.agents.middleware import TodoListMiddleware
from langchain_anthropic.middleware import AnthropicPromptCachingMiddleware
Expand All @@ -34,18 +33,24 @@
from sqlalchemy.ext.asyncio import AsyncSession

from app.agents.new_chat.context import SurfSenseContextSchema
from app.agents.new_chat.filesystem_backends import build_backend_resolver
from app.agents.new_chat.filesystem_selection import FilesystemSelection
from app.agents.new_chat.llm_config import AgentConfig
from app.agents.new_chat.middleware import (
DedupHITLToolCallsMiddleware,
FileIntentMiddleware,
KnowledgeBaseSearchMiddleware,
MemoryInjectionMiddleware,
SurfSenseFilesystemMiddleware,
)
from app.agents.new_chat.middleware.safe_summarization import (
create_safe_summarization_middleware,
)
from app.agents.new_chat.system_prompt import (
build_configurable_system_prompt,
build_surfsense_system_prompt,
)
from app.agents.new_chat.tools.registry import build_tools_async
from app.agents.new_chat.tools.registry import build_tools_async, get_connector_gated_tools
from app.db import ChatVisibility
from app.services.connector_service import ConnectorService
from app.utils.perf import get_perf_logger
Expand Down Expand Up @@ -162,6 +167,7 @@ async def create_surfsense_deep_agent(
thread_visibility: ChatVisibility | None = None,
mentioned_document_ids: list[int] | None = None,
anon_session_id: str | None = None,
filesystem_selection: FilesystemSelection | None = None,
):
"""
Create a SurfSense deep agent with configurable tools and prompts.
Expand Down Expand Up @@ -236,6 +242,8 @@ async def create_surfsense_deep_agent(
)
"""
_t_agent_total = time.perf_counter()
filesystem_selection = filesystem_selection or FilesystemSelection()
backend_resolver = build_backend_resolver(filesystem_selection)

# Discover available connectors and document types for this search space
available_connectors: list[str] | None = None
Expand Down Expand Up @@ -285,105 +293,10 @@ async def create_surfsense_deep_agent(
"llm": llm,
}

# Disable Notion action tools if no Notion connector is configured
modified_disabled_tools = list(disabled_tools) if disabled_tools else []
has_notion_connector = (
available_connectors is not None and "NOTION_CONNECTOR" in available_connectors
)
if not has_notion_connector:
notion_tools = [
"create_notion_page",
"update_notion_page",
"delete_notion_page",
]
modified_disabled_tools.extend(notion_tools)

# Disable Linear action tools if no Linear connector is configured
has_linear_connector = (
available_connectors is not None and "LINEAR_CONNECTOR" in available_connectors
)
if not has_linear_connector:
linear_tools = [
"create_linear_issue",
"update_linear_issue",
"delete_linear_issue",
]
modified_disabled_tools.extend(linear_tools)

# Disable Google Drive action tools if no Google Drive connector is configured
has_google_drive_connector = (
available_connectors is not None and "GOOGLE_DRIVE_FILE" in available_connectors
)
if not has_google_drive_connector:
google_drive_tools = [
"create_google_drive_file",
"delete_google_drive_file",
]
modified_disabled_tools.extend(google_drive_tools)

has_dropbox_connector = (
available_connectors is not None and "DROPBOX_FILE" in available_connectors
)
if not has_dropbox_connector:
modified_disabled_tools.extend(["create_dropbox_file", "delete_dropbox_file"])

has_onedrive_connector = (
available_connectors is not None and "ONEDRIVE_FILE" in available_connectors
)
if not has_onedrive_connector:
modified_disabled_tools.extend(["create_onedrive_file", "delete_onedrive_file"])

# Disable Google Calendar action tools if no Google Calendar connector is configured
has_google_calendar_connector = (
available_connectors is not None
and "GOOGLE_CALENDAR_CONNECTOR" in available_connectors
)
if not has_google_calendar_connector:
calendar_tools = [
"create_calendar_event",
"update_calendar_event",
"delete_calendar_event",
]
modified_disabled_tools.extend(calendar_tools)

# Disable Gmail action tools if no Gmail connector is configured
has_gmail_connector = (
available_connectors is not None
and "GOOGLE_GMAIL_CONNECTOR" in available_connectors
)
if not has_gmail_connector:
gmail_tools = [
"create_gmail_draft",
"update_gmail_draft",
"send_gmail_email",
"trash_gmail_email",
]
modified_disabled_tools.extend(gmail_tools)

# Disable Jira action tools if no Jira connector is configured
has_jira_connector = (
available_connectors is not None and "JIRA_CONNECTOR" in available_connectors
)
if not has_jira_connector:
jira_tools = [
"create_jira_issue",
"update_jira_issue",
"delete_jira_issue",
]
modified_disabled_tools.extend(jira_tools)

# Disable Confluence action tools if no Confluence connector is configured
has_confluence_connector = (
available_connectors is not None
and "CONFLUENCE_CONNECTOR" in available_connectors
modified_disabled_tools.extend(
get_connector_gated_tools(available_connectors)
)
if not has_confluence_connector:
confluence_tools = [
"create_confluence_page",
"update_confluence_page",
"delete_confluence_page",
]
modified_disabled_tools.extend(confluence_tools)

# Remove direct KB search tool; we now pre-seed a scoped filesystem via middleware.
if "search_knowledge_base" not in modified_disabled_tools:
Expand All @@ -407,6 +320,20 @@ async def create_surfsense_deep_agent(
_t0 = time.perf_counter()
_enabled_tool_names = {t.name for t in tools}
_user_disabled_tool_names = set(disabled_tools) if disabled_tools else set()

# Collect generic MCP connector info so the system prompt can route queries
# to their tools instead of falling back to "not in knowledge base".
_mcp_connector_tools: dict[str, list[str]] = {}
for t in tools:
meta = getattr(t, "metadata", None) or {}
if meta.get("mcp_is_generic") and meta.get("mcp_connector_name"):
_mcp_connector_tools.setdefault(
meta["mcp_connector_name"], [],
).append(t.name)

if _mcp_connector_tools:
_perf_log.info("MCP connector tool routing: %s", _mcp_connector_tools)

if agent_config is not None:
system_prompt = build_configurable_system_prompt(
custom_system_instructions=agent_config.system_instructions,
Expand All @@ -415,12 +342,14 @@ async def create_surfsense_deep_agent(
thread_visibility=thread_visibility,
enabled_tool_names=_enabled_tool_names,
disabled_tool_names=_user_disabled_tool_names,
mcp_connector_tools=_mcp_connector_tools,
)
else:
system_prompt = build_surfsense_system_prompt(
thread_visibility=thread_visibility,
enabled_tool_names=_enabled_tool_names,
disabled_tool_names=_user_disabled_tool_names,
mcp_connector_tools=_mcp_connector_tools,
)
_perf_log.info(
"[create_agent] System prompt built in %.3fs", time.perf_counter() - _t0
Expand All @@ -437,12 +366,15 @@ async def create_surfsense_deep_agent(
gp_middleware = [
TodoListMiddleware(),
_memory_middleware,
FileIntentMiddleware(llm=llm),
SurfSenseFilesystemMiddleware(
backend=backend_resolver,
filesystem_mode=filesystem_selection.mode,
search_space_id=search_space_id,
created_by_id=user_id,
thread_id=thread_id,
),
create_summarization_middleware(llm, StateBackend),
create_safe_summarization_middleware(llm, StateBackend),
PatchToolCallsMiddleware(),
AnthropicPromptCachingMiddleware(unsupported_model_behavior="ignore"),
]
Expand All @@ -458,21 +390,25 @@ async def create_surfsense_deep_agent(
deepagent_middleware = [
TodoListMiddleware(),
_memory_middleware,
FileIntentMiddleware(llm=llm),
KnowledgeBaseSearchMiddleware(
llm=llm,
search_space_id=search_space_id,
filesystem_mode=filesystem_selection.mode,
available_connectors=available_connectors,
available_document_types=available_document_types,
mentioned_document_ids=mentioned_document_ids,
anon_session_id=anon_session_id,
),
SurfSenseFilesystemMiddleware(
backend=backend_resolver,
filesystem_mode=filesystem_selection.mode,
search_space_id=search_space_id,
created_by_id=user_id,
thread_id=thread_id,
),
SubAgentMiddleware(backend=StateBackend, subagents=[general_purpose_spec]),
create_summarization_middleware(llm, StateBackend),
create_safe_summarization_middleware(llm, StateBackend),
PatchToolCallsMiddleware(),
DedupHITLToolCallsMiddleware(agent_tools=tools),
AnthropicPromptCachingMiddleware(unsupported_model_behavior="ignore"),
Expand Down
13 changes: 12 additions & 1 deletion surfsense_backend/app/agents/new_chat/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@
This module defines the custom state schema used by the SurfSense deep agent.
"""

from typing import TypedDict
from typing import NotRequired, TypedDict


class FileOperationContractState(TypedDict):
intent: str
confidence: float
suggested_path: str
timestamp: str
turn_id: str


class SurfSenseContextSchema(TypedDict):
Expand All @@ -24,5 +32,8 @@ class SurfSenseContextSchema(TypedDict):
"""

search_space_id: int
file_operation_contract: NotRequired[FileOperationContractState]
turn_id: NotRequired[str]
request_id: NotRequired[str]
# These are runtime-injected and won't be serialized
# db_session and connector_service are passed when invoking the agent
42 changes: 42 additions & 0 deletions surfsense_backend/app/agents/new_chat/filesystem_backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Filesystem backend resolver for cloud and desktop-local modes."""

from __future__ import annotations

from collections.abc import Callable
from functools import lru_cache

from deepagents.backends.state import StateBackend
from langgraph.prebuilt.tool_node import ToolRuntime

from app.agents.new_chat.filesystem_selection import FilesystemMode, FilesystemSelection
from app.agents.new_chat.middleware.multi_root_local_folder_backend import (
MultiRootLocalFolderBackend,
)


@lru_cache(maxsize=64)
def _cached_multi_root_backend(
mounts: tuple[tuple[str, str], ...],
) -> MultiRootLocalFolderBackend:
return MultiRootLocalFolderBackend(mounts)


def build_backend_resolver(
selection: FilesystemSelection,
) -> Callable[[ToolRuntime], StateBackend | MultiRootLocalFolderBackend]:
"""Create deepagents backend resolver for the selected filesystem mode."""

if selection.mode == FilesystemMode.DESKTOP_LOCAL_FOLDER and selection.local_mounts:

def _resolve_local(_runtime: ToolRuntime) -> MultiRootLocalFolderBackend:
mounts = tuple(
(entry.mount_id, entry.root_path) for entry in selection.local_mounts
)
return _cached_multi_root_backend(mounts)

return _resolve_local

def _resolve_cloud(runtime: ToolRuntime) -> StateBackend:
return StateBackend(runtime)

return _resolve_cloud
Loading
Loading