Repository navigation
bug(desktop): WebSearch is exposed to the model even when disabled, unconfigured, or in privacy mode #2085
Description
What happened
The WebSearch tool is advertised to the model in the tool list every turn even when it cannot possibly run. Availability is only checked inside the tool's impl/search() — i.e. at call time — so the model spends a turn (and tokens) calling a tool that is guaranteed to fail:
- Desktop path:
apps/desktop/src/main/tool-assembly.tsbuildsbuildWebSearchAgentTool()unconditionally intobuiltinTools; the settings/credential/privacy checks inapps/desktop/src/main/web-search/agent-tool.tsall live inimpl, returningweb_search_error { reason: not_configured | incognito_active }. - Host path:
packages/runtime-host/src/server/web-search-tool.tshas the same shape —resolveWebSearchExecution()is only consulted insidesearch().
The Settings · Web search toggle (webSearch.enabled, which defaults to false), the Tavily credential status, and privacy/incognito mode have zero effect on the model-visible surface. This contradicts the tool's own fail-closed documentation ("Fail-closed paths" in agent-tool.ts) and its description, which actively invites calls ("Use ONLY when the user asks for current external information").
How to reproduce
- Leave web search disabled (the default), or unset the Tavily key, or enable privacy mode.
- Start a session with a real backend.
- Ask something that could benefit from current external information.
- Expected: the model never sees
WebSearchin its tool list (or the surface marks it unavailable), so it never attempts it. - Actual:
WebSearchis in the tool list every turn; the model calls it and gets aweb_search_errorback, wasting a turn. In deep-research sessionsWebSearchis also inDEEP_RESEARCH_ALLOWED_TOOL_NAMES(desktop-backend-tool-surface.ts), so a disabled web search silently cripples the DR toolset too.
Suggested direction
The availability logic already exists and returns rich states: resolveWebSearchExecution() in packages/storage/src/runtime-policy/coordinator.ts:535 yields privacy_mode | disabled | credential_not_configured | ready. It is simply only consulted at call time. Options:
- Filter
WebSearchout of the effective tool surface (tool-assembly.ts/desktop-backend-tool-surface.ts, and the host'sexecution-model-composition.ts) based on the same check, recomputed per turn so mid-session toggle changes take effect. - Alternatively, keep the tool but attach availability metadata to the surface so the model knows not to call it — though hiding is closer to the existing fail-closed posture and to how capability-gated tools are handled.
Also worth deciding: Settings · Web search wording should state when the tool is hidden from the model vs. merely failing at call time.
Environment
- Surface: Desktop (Runtime Host shares the same pattern)
- Commit: current
main
Logs, screenshots, or additional context
apps/desktop/src/main/tool-assembly.ts—webSearchTooladded totoolsAfterSkill/builtinToolsunconditionallyapps/desktop/src/main/web-search/agent-tool.ts— fail-closed checks only insideimplpackages/runtime-host/src/server/web-search-tool.ts—resolveWebSearchExecution()only insidesearch()packages/storage/src/runtime-policy/coordinator.ts—resolveWebSearchExecution(), the existing availability seam
Activity
Benjamin-eecs commentedon Aug 4, 2026
#2152 already routes WebSearch at the tool surface and drops it when disabled or in privacy mode. Still open after it: a missing Tavily key keeps the tool visible, and cached backends do not refresh on a settings flip. I have a fix for those on a local branch; holding it until #2152 settles.