diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/constants.py b/surfsense_backend/app/agents/chat/multi_agent_chat/constants.py
index 7e4061813..fff608742 100644
--- a/surfsense_backend/app/agents/chat/multi_agent_chat/constants.py
+++ b/surfsense_backend/app/agents/chat/multi_agent_chat/constants.py
@@ -26,6 +26,7 @@ CONNECTOR_TYPE_TO_CONNECTOR_AGENT_MAPS: dict[str, str] = {
SUBAGENT_TO_REQUIRED_CONNECTOR_MAP: dict[str, frozenset[str]] = {
"deliverables": frozenset(),
"knowledge_base": frozenset(),
+ "intelligence_agent": frozenset(),
"airtable": frozenset({"AIRTABLE_CONNECTOR"}),
"calendar": frozenset({"GOOGLE_CALENDAR_CONNECTOR"}),
"clickup": frozenset({"CLICKUP_CONNECTOR"}),
diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/__init__.py b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/agent.py b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/agent.py
new file mode 100644
index 000000000..21933005b
--- /dev/null
+++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/agent.py
@@ -0,0 +1,43 @@
+"""``intelligence_agent`` route: ``SurfSenseSubagentSpec`` builder for deepagents."""
+
+from __future__ import annotations
+
+from typing import Any
+
+from langchain_core.language_models import BaseChatModel
+from langchain_core.tools import BaseTool
+
+from app.agents.chat.multi_agent_chat.subagents.shared.md_file_reader import (
+ read_md_file,
+)
+from app.agents.chat.multi_agent_chat.subagents.shared.spec import SurfSenseSubagentSpec
+from app.agents.chat.multi_agent_chat.subagents.shared.subagent_builder import (
+ pack_subagent,
+)
+
+from .tools.index import NAME, RULESET, load_tools
+
+
+def build_subagent(
+ *,
+ dependencies: dict[str, Any],
+ model: BaseChatModel | None = None,
+ middleware_stack: dict[str, Any] | None = None,
+ mcp_tools: list[BaseTool] | None = None,
+) -> SurfSenseSubagentSpec:
+ tools = [*load_tools(dependencies=dependencies), *(mcp_tools or [])]
+ description = (
+ read_md_file(__package__, "description").strip()
+ or "Gathers and compares web intelligence for this workspace."
+ )
+ system_prompt = read_md_file(__package__, "system_prompt").strip()
+ return pack_subagent(
+ name=NAME,
+ description=description,
+ system_prompt=system_prompt,
+ tools=tools,
+ ruleset=RULESET,
+ dependencies=dependencies,
+ model=model,
+ middleware_stack=middleware_stack,
+ )
diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/description.md b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/description.md
new file mode 100644
index 000000000..cefad396c
--- /dev/null
+++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/description.md
@@ -0,0 +1,2 @@
+Specialist for web intelligence: fetch live pages and search the web, then compare against earlier findings.
+Use whenever a task needs current data pulled from specific URLs or discovered on the web — competitor/product monitoring, pricing, listings, "what changed since last time" — and answered from that evidence.
diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/system_prompt.md b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/system_prompt.md
new file mode 100644
index 000000000..0289f40e2
--- /dev/null
+++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/system_prompt.md
@@ -0,0 +1,54 @@
+You are the SurfSense intelligence sub-agent.
+You receive delegated instructions from a supervisor agent and return structured results for supervisor synthesis.
+
+
+Pull evidence from the live web with the web.* capability verbs and answer the delegated question, including "what changed" comparisons against evidence already in this conversation.
+
+
+
+- `web_discover` — search the web for a query; returns ranked hits (url, title, snippet, provider).
+- `web_scrape` — fetch specific URLs; returns clean page content and metadata per URL.
+
+
+
+- If the request names exact URLs, call `web_scrape` on them directly.
+- If it does not, call `web_discover` first, pick the most relevant hits, then `web_scrape` those URLs to read them.
+- Batch URLs into a single `web_scrape` call when reading several pages (up to its limit) instead of many one-URL calls.
+- For "what changed" / monitoring requests, compare the freshly scraped values against the prior values already present in this conversation's earlier tool results, and report the concrete deltas (added, removed, changed old -> new). Do not claim a change you cannot point to.
+
+
+
+- Use only tools in ``.
+- A `web_scrape` row with `status` other than `success` yielded no content — do not invent its content; report it as unavailable.
+- Never fabricate facts, URLs, prices, or quotes.
+
+
+
+- Report uncertainty explicitly when evidence is incomplete or conflicting.
+- Never present unverified claims as facts.
+
+
+
+- On tool failure, return `status=error` with a concise recovery `next_step`.
+- On no useful evidence, return `status=blocked` with a narrower query or the URLs you still need.
+
+
+
+Return **only** one JSON object (no markdown/prose):
+{
+ "status": "success" | "partial" | "blocked" | "error",
+ "action_summary": string,
+ "evidence": {
+ "findings": string[],
+ "sources": string[],
+ "confidence": "high" | "medium" | "low"
+ },
+ "next_step": string | null,
+ "missing_fields": string[] | null,
+ "assumptions": string[] | null
+}
+
+Route-specific rules:
+- `evidence.findings`: max 10 entries, each a single sentence stating one distinct fact or delta. Do not paste raw scraped pages.
+- `evidence.sources`: max 10 URLs, one per finding when applicable. List each URL once.
+
diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/tools/__init__.py b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/tools/__init__.py
new file mode 100644
index 000000000..e06e600b5
--- /dev/null
+++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/tools/__init__.py
@@ -0,0 +1 @@
+"""Intelligence-agent tools: the web.* capability verbs, generated from the registry."""
diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/tools/index.py b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/tools/index.py
new file mode 100644
index 000000000..f0b088dc1
--- /dev/null
+++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/intelligence_agent/tools/index.py
@@ -0,0 +1,28 @@
+"""``intelligence_agent`` tools: web.* verbs generated from the capability registry."""
+
+from __future__ import annotations
+
+from typing import Any
+
+from langchain_core.tools import BaseTool
+
+from app.agents.chat.multi_agent_chat.shared.permissions import Ruleset
+from app.capabilities.access.agent import build_capability_tools
+from app.capabilities.web.discover.definition import WEB_DISCOVER
+from app.capabilities.web.scrape.definition import WEB_SCRAPE
+
+NAME = "intelligence_agent"
+
+RULESET = Ruleset(origin=NAME, rules=[])
+
+_CI_VERBS = [WEB_DISCOVER, WEB_SCRAPE]
+
+
+def load_tools(
+ *, dependencies: dict[str, Any] | None = None, **kwargs: Any
+) -> list[BaseTool]:
+ d = {**(dependencies or {}), **kwargs}
+ return build_capability_tools(
+ workspace_id=d.get("workspace_id"),
+ capabilities=_CI_VERBS,
+ )
diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/registry.py b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/registry.py
index c48b7f7ac..652ca079e 100644
--- a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/registry.py
+++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/registry.py
@@ -15,6 +15,9 @@ from app.agents.chat.multi_agent_chat.constants import (
from app.agents.chat.multi_agent_chat.subagents.builtins.deliverables.agent import (
build_subagent as build_deliverables_subagent,
)
+from app.agents.chat.multi_agent_chat.subagents.builtins.intelligence_agent.agent import (
+ build_subagent as build_intelligence_agent_subagent,
+)
from app.agents.chat.multi_agent_chat.subagents.builtins.knowledge_base.agent import (
build_subagent as build_knowledge_base_subagent,
)
@@ -99,6 +102,7 @@ SUBAGENT_BUILDERS_BY_NAME: dict[str, SubagentBuilder] = {
"dropbox": build_dropbox_subagent,
"gmail": build_gmail_subagent,
"google_drive": build_google_drive_subagent,
+ "intelligence_agent": build_intelligence_agent_subagent,
"jira": build_jira_subagent,
"knowledge_base": build_knowledge_base_subagent,
"linear": build_linear_subagent,
diff --git a/surfsense_backend/tests/unit/agents/multi_agent_chat/test_subagent_composition.py b/surfsense_backend/tests/unit/agents/multi_agent_chat/test_subagent_composition.py
index 157f1703b..68ae05bbd 100644
--- a/surfsense_backend/tests/unit/agents/multi_agent_chat/test_subagent_composition.py
+++ b/surfsense_backend/tests/unit/agents/multi_agent_chat/test_subagent_composition.py
@@ -19,7 +19,7 @@ from app.agents.chat.multi_agent_chat.subagents.registry import (
pytestmark = pytest.mark.unit
-# The full specialist roster the main agent composes from: 4 builtins + 15
+# The full specialist roster the main agent composes from: 5 builtins + 15
# connector routes. Adding/removing a specialist is a deliberate product change
# and must be reflected here.
_EXPECTED_SUBAGENTS = frozenset(
@@ -33,6 +33,7 @@ _EXPECTED_SUBAGENTS = frozenset(
"dropbox",
"gmail",
"google_drive",
+ "intelligence_agent",
"jira",
"knowledge_base",
"linear",