Wire Linear and Slack specialists and prompt routing.

This commit is contained in:
CREDO23 2026-04-29 20:51:06 +02:00
parent 41cb4a567b
commit bf9b606a61
8 changed files with 53 additions and 8 deletions

View file

@ -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.
</tool_routing>

View file

@ -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.
</tool_routing>

View file

@ -1 +1,3 @@
<linear_routing>
**Linear:** Prefer the `task` tool with subagent **`linear_specialist`** when the users 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.
</linear_routing>

View file

@ -1 +1,3 @@
<slack_routing>
**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.
</slack_routing>

View file

@ -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",
]

View file

@ -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
),

View file

@ -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,
)

View file

@ -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)