diff --git a/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_private.md b/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_private.md
index ec667bf88..b8bb069e2 100644
--- a/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_private.md
+++ b/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_private.md
@@ -5,12 +5,19 @@ say "I don't see it in the knowledge base" or ask the user if they want you to c
Ignore any knowledge base results for these services.
When to use which tool:
-- Linear (issues) → list_issues, get_issue, save_issue (create/update)
+- Linear (issues, teams, users, projects when MCP exposes them) → hosted Linear MCP read tools (e.g. `list_issues`, `get_issue`, `list_teams`, `list_users`, …) and `save_issue` for create/update; native SurfSense Linear issue tools when present. For **multi-step Linear-only** work (several reads, structured evidence), delegate with the `task` tool to subagent **`linear_specialist`** instead of mixing unrelated tools.
- ClickUp (tasks) → clickup_search, clickup_get_task
- Jira (issues) → getAccessibleAtlassianResources (cloudId discovery), getVisibleJiraProjects (project discovery), getJiraProjectIssueTypesMetadata (issue type discovery), searchJiraIssuesUsingJql, createJiraIssue, editJiraIssue
-- Slack (messages, channels) → slack_search_channels, slack_read_channel, slack_read_thread
+- Slack (messages, channels) → `slack_search_channels`, `slack_read_channel`, `slack_read_thread`, and other `slack_*` tools when connected. For **multi-step Slack-only** work, delegate with `task` to **`slack_specialist`**.
- Airtable (bases, tables, records) → list_bases, list_tables_for_base, list_records_for_table
- Knowledge base content (Notion, GitHub, files, notes) → automatically searched
- Real-time public web data → call web_search
- Reading a specific webpage → call scrape_webpage
+
+**`task` subagents (when to delegate):**
+- **`linear_specialist`** — Linear-only investigations and tool use.
+- **`slack_specialist`** — Slack-only investigations and tool use.
+- **`connector_negotiator`** — **Cross-connector** chains (e.g. data from Slack then action in Linear).
+- **`explore`** — Read-only KB + web research with citations.
+- **`report_writer`** — Single `generate_report` deliverable.
diff --git a/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_team.md b/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_team.md
index 48b7a990b..b081a2123 100644
--- a/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_team.md
+++ b/surfsense_backend/app/agents/new_chat/prompts/base/tool_routing_team.md
@@ -5,12 +5,19 @@ say "I don't see it in the knowledge base" or ask if they want you to check.
Ignore any knowledge base results for these services.
When to use which tool:
-- Linear (issues) → list_issues, get_issue, save_issue (create/update)
+- Linear (issues, teams, users, projects when MCP exposes them) → hosted Linear MCP read tools (e.g. `list_issues`, `get_issue`, `list_teams`, `list_users`, …) and `save_issue` for create/update; native SurfSense Linear issue tools when present. For **multi-step Linear-only** work (several reads, structured evidence), delegate with the `task` tool to subagent **`linear_specialist`** instead of mixing unrelated tools.
- ClickUp (tasks) → clickup_search, clickup_get_task
- Jira (issues) → getAccessibleAtlassianResources (cloudId discovery), getVisibleJiraProjects (project discovery), getJiraProjectIssueTypesMetadata (issue type discovery), searchJiraIssuesUsingJql, createJiraIssue, editJiraIssue
-- Slack (messages, channels) → slack_search_channels, slack_read_channel, slack_read_thread
+- Slack (messages, channels) → `slack_search_channels`, `slack_read_channel`, `slack_read_thread`, and other `slack_*` tools when connected. For **multi-step Slack-only** work, delegate with `task` to **`slack_specialist`**.
- Airtable (bases, tables, records) → list_bases, list_tables_for_base, list_records_for_table
- Knowledge base content (Notion, GitHub, files, notes) → automatically searched
- Real-time public web data → call web_search
- Reading a specific webpage → call scrape_webpage
+
+**`task` subagents (when to delegate):**
+- **`linear_specialist`** — Linear-only investigations and tool use.
+- **`slack_specialist`** — Slack-only investigations and tool use.
+- **`connector_negotiator`** — **Cross-connector** chains (e.g. data from Slack then action in Linear).
+- **`explore`** — Read-only KB + web research with citations.
+- **`report_writer`** — Single `generate_report` deliverable.
diff --git a/surfsense_backend/app/agents/new_chat/prompts/routing/linear.md b/surfsense_backend/app/agents/new_chat/prompts/routing/linear.md
index 8b1378917..2f1bfacd9 100644
--- a/surfsense_backend/app/agents/new_chat/prompts/routing/linear.md
+++ b/surfsense_backend/app/agents/new_chat/prompts/routing/linear.md
@@ -1 +1,3 @@
-
+
+**Linear:** Prefer the `task` tool with subagent **`linear_specialist`** when the user’s request is **only about Linear** and may need several tool calls (list issues, inspect one issue, teams, users, statuses, comments, documents). Use **`connector_negotiator`** when Linear is one hop in a **multi-connector** workflow. Call Linear MCP tools directly from the parent when a **single** quick call is enough.
+
diff --git a/surfsense_backend/app/agents/new_chat/prompts/routing/slack.md b/surfsense_backend/app/agents/new_chat/prompts/routing/slack.md
index 8b1378917..4b5d07a9a 100644
--- a/surfsense_backend/app/agents/new_chat/prompts/routing/slack.md
+++ b/surfsense_backend/app/agents/new_chat/prompts/routing/slack.md
@@ -1 +1,3 @@
-
+
+**Slack:** Prefer `task` with **`slack_specialist`** for **Slack-only** multi-step work (channels, threads, reads, writes that need approval in the specialist). Use **`connector_negotiator`** when Slack feeds another connector in one chain. Use direct `slack_*` tools from the parent for a **single** quick read or write when appropriate.
+
diff --git a/surfsense_backend/app/agents/new_chat/subagents/__init__.py b/surfsense_backend/app/agents/new_chat/subagents/__init__.py
index 7d678ec79..bd1823b57 100644
--- a/surfsense_backend/app/agents/new_chat/subagents/__init__.py
+++ b/surfsense_backend/app/agents/new_chat/subagents/__init__.py
@@ -20,10 +20,14 @@ from .config import (
build_report_writer_subagent,
build_specialized_subagents,
)
+from .providers.linear import build_linear_specialist_subagent
+from .providers.slack import build_slack_specialist_subagent
__all__ = [
"build_connector_negotiator_subagent",
"build_explore_subagent",
+ "build_linear_specialist_subagent",
"build_report_writer_subagent",
+ "build_slack_specialist_subagent",
"build_specialized_subagents",
]
diff --git a/surfsense_backend/app/agents/new_chat/subagents/config.py b/surfsense_backend/app/agents/new_chat/subagents/config.py
index b36d35fa0..78436f674 100644
--- a/surfsense_backend/app/agents/new_chat/subagents/config.py
+++ b/surfsense_backend/app/agents/new_chat/subagents/config.py
@@ -22,6 +22,12 @@ from typing import TYPE_CHECKING, Any
from app.agents.new_chat.middleware.skills_backends import default_skills_sources
from app.agents.new_chat.permissions import Rule, Ruleset
+from app.agents.new_chat.subagents.providers.linear import (
+ build_linear_specialist_subagent,
+)
+from app.agents.new_chat.subagents.providers.slack import (
+ build_slack_specialist_subagent,
+)
if TYPE_CHECKING:
from deepagents import SubAgent
@@ -419,6 +425,12 @@ def build_specialized_subagents(
build_report_writer_subagent(
tools=tools, model=model, extra_middleware=extra_middleware
),
+ build_linear_specialist_subagent(
+ tools=tools, model=model, extra_middleware=extra_middleware
+ ),
+ build_slack_specialist_subagent(
+ tools=tools, model=model, extra_middleware=extra_middleware
+ ),
build_connector_negotiator_subagent(
tools=tools, model=model, extra_middleware=extra_middleware
),
diff --git a/surfsense_backend/app/agents/new_chat/system_prompt.py b/surfsense_backend/app/agents/new_chat/system_prompt.py
index 56f838d7e..70634c65d 100644
--- a/surfsense_backend/app/agents/new_chat/system_prompt.py
+++ b/surfsense_backend/app/agents/new_chat/system_prompt.py
@@ -26,6 +26,9 @@ from .prompts.composer import (
detect_provider_variant,
)
+# Optional routing fragments under ``prompts/routing/`` (see composer).
+_DEFAULT_CONNECTOR_ROUTING: tuple[str, ...] = ("linear", "slack")
+
# Public re-exports for backwards compatibility (some legacy code reads the
# raw default-instructions text directly).
SURFSENSE_SYSTEM_INSTRUCTIONS_TEMPLATE = (
@@ -63,6 +66,7 @@ def build_surfsense_system_prompt(
mcp_connector_tools=mcp_connector_tools,
citations_enabled=True,
model_name=model_name,
+ connector_routing=_DEFAULT_CONNECTOR_ROUTING,
)
@@ -93,6 +97,7 @@ def build_configurable_system_prompt(
use_default_system_instructions=use_default_system_instructions,
citations_enabled=citations_enabled,
model_name=model_name,
+ connector_routing=_DEFAULT_CONNECTOR_ROUTING,
)
diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_specialized_subagents.py b/surfsense_backend/tests/unit/agents/new_chat/test_specialized_subagents.py
index 0adb578ce..3035cc8e0 100644
--- a/surfsense_backend/tests/unit/agents/new_chat/test_specialized_subagents.py
+++ b/surfsense_backend/tests/unit/agents/new_chat/test_specialized_subagents.py
@@ -210,10 +210,16 @@ class TestConnectorNegotiatorSubagent:
class TestBuildSpecializedSubagents:
- def test_returns_three_specs(self) -> None:
+ def test_returns_five_specs(self) -> None:
specs = build_specialized_subagents(tools=ALL_TOOLS)
names = [s["name"] for s in specs] # type: ignore[index]
- assert names == ["explore", "report_writer", "connector_negotiator"]
+ assert names == [
+ "explore",
+ "report_writer",
+ "linear_specialist",
+ "slack_specialist",
+ "connector_negotiator",
+ ]
def test_all_specs_have_unique_names(self) -> None:
specs = build_specialized_subagents(tools=ALL_TOOLS)