can1357--oh-my-pi
32 KiB
32 KiB
web_search
Run one web query through the first available search provider and return LLM-formatted answer, source URLs, and optional citations.
Source
- Entry:
packages/coding-agent/src/web/search/index.ts - Model-facing prompt:
packages/coding-agent/src/prompts/tools/web-search.md - Key collaborators:
packages/coding-agent/src/web/search/provider.ts— lazy provider registry; availability chain.packages/coding-agent/src/web/search/types.ts— unifiedSearchResponse/SearchProviderErrortypes.packages/coding-agent/src/web/search/render.ts— TUI renderer details type.packages/coding-agent/src/web/search/providers/base.ts— provider interface and shared params contract.packages/coding-agent/src/web/search/providers/utils.ts— credential lookup; source normalization.packages/coding-agent/src/web/search/providers/browser-headers.ts— shared Chromium navigation headers for scrape providers.packages/coding-agent/src/web/search/providers/browser-page.ts— shared fetch/headless-browser page loader for scrape providers.packages/coding-agent/src/web/search/providers/anthropic.ts— Claude web-search provider.packages/coding-agent/src/web/search/providers/bing.ts— Bing HTML SERP scraper.packages/coding-agent/src/web/search/providers/brave.ts— Brave Search API adapter.packages/coding-agent/src/web/search/providers/codex.ts— OpenAI Codex SSE adapter.packages/coding-agent/src/web/search/providers/duckduckgo.ts— DuckDuckGo HTML frontend scraper.packages/coding-agent/src/web/search/providers/ecosia.ts— Ecosia browser-backed scraper.packages/coding-agent/src/web/search/providers/exa.ts— Exa API or MCP adapter.packages/coding-agent/src/web/search/providers/firecrawl.ts— Firecrawl search adapter.packages/coding-agent/src/web/search/providers/gemini.ts— Gemini grounding SSE adapter.packages/coding-agent/src/web/search/providers/google.ts— Google browser-backed SERP scraper.packages/coding-agent/src/web/search/providers/jina.ts— Jina Reader search adapter.packages/coding-agent/src/web/search/providers/kagi.ts— Kagi provider wrapper.packages/coding-agent/src/web/search/providers/kimi.ts— Kimi search adapter.packages/coding-agent/src/web/search/providers/mojeek.ts— Mojeek browser-backed scraper (independent index).packages/coding-agent/src/web/search/providers/parallel.ts— Parallel provider wrapper.packages/coding-agent/src/web/search/providers/perplexity.ts— Perplexity API / OAuth adapter.packages/coding-agent/src/web/search/providers/public.ts— Public Web aggregate over all credential-free engines.packages/coding-agent/src/web/search/providers/searxng.ts— self-hosted SearXNG adapter.packages/coding-agent/src/web/search/providers/startpage.ts— Startpage (Google-proxied) form-flow scraper.packages/coding-agent/src/web/search/providers/synthetic.ts— Synthetic search adapter.packages/coding-agent/src/web/search/providers/tavily.ts— Tavily search adapter.packages/coding-agent/src/web/search/providers/tinyfish.ts— TinyFish search adapter.packages/coding-agent/src/web/search/providers/xai.ts— xAI Responses web-search adapter.packages/coding-agent/src/web/search/providers/yahoo.ts— Yahoo HTML SERP scraper.packages/coding-agent/src/web/search/providers/zai.ts— Z.AI remote MCP adapter.packages/coding-agent/src/web/parallel.ts— Parallel search/extract HTTP client.packages/coding-agent/src/web/kagi.ts— Kagi HTTP client.packages/coding-agent/src/tools/index.ts— built-in tool registration and enable flag.
Inputs
| Field | Type | Required | Description |
|---|---|---|---|
query |
string |
Yes | Search query, passed to providers unchanged. |
recency |
"day" | "week" | "month" | "year" |
No | Time filter. Only providers that implement it use it; code maps it for Brave, Perplexity, Tavily, SearXNG, Kagi, TinyFish, Firecrawl, and xAI. |
limit |
number |
No | Max results to return. Usually becomes the provider request's result-count parameter when num_search_results is absent. TinyFish uses it for paginated fetches before slicing; xAI sends it as search_parameters.max_search_results when num_search_results is absent and also caps parsed sources/citations locally, defaulting to 10 and max 30. |
max_tokens |
number |
No | Passed through as provider token caps (maxOutputTokens, max_tokens, or xAI max_output_tokens) only by Anthropic, Gemini, xAI, and Perplexity API-key mode. Ignored by the other providers. |
temperature |
number |
No | Passed through only by Anthropic, Gemini, xAI, and Perplexity API-key mode. Ignored by the other providers. |
num_search_results |
number |
No | Requested search breadth or local result cap. Most providers send it upstream. TinyFish clamps to 1..20 with default 10, sends it as num_results per page, and uses paginated fetches before slicing. xAI sends it as search_parameters.max_search_results and caps parsed sources/citations locally with default 10 and max 30. |
Outputs
The tool returns a single text content block plus structured details.
content:[{ type: "text", text: string }]details:SearchRenderDetailsfrompackages/coding-agent/src/web/search/render.tsresponse: SearchResponseerror?: string
text is produced by formatForLLM() in packages/coding-agent/src/web/search/index.ts:
- If
response.answerexists, it is emitted first. - If sources exist, one entry per source follows (the
## Sourcesheader with a source count is emitted only when an answer was also produced):[n] <title> (<formatted age or published date>)<url>- optional snippet line truncated to 240 chars.
- If citations exist, a
## Citationssection follows with URL/title plus optional cited text truncated to 240 chars. - If related questions exist, a
## Relatedbullet list follows. - If search queries exist, a
Search queries: <n>section follows, capped to the first 3 queries and 120 chars each.
Failure output is not thrown at the tool boundary when providers are unavailable or provider attempts fail. Instead the tool returns:
content[0].text = "Error: ..."details.response.provider = <last attempted provider> | "none"details.error = ...
Streaming: none. WebSearchTool.execute() forwards its AbortSignal into executeSearch(), and executeSearch() passes it to providers. If the signal is aborted during fallback handling, throwIfAborted(signal) rethrows the cancellation instead of returning an "Error: ..." text result.
Flow
WebSearchTool.execute()inpackages/coding-agent/src/web/search/index.tsdelegates directly toexecuteSearch().executeSearch()chooses a provider list:- if
params.provideris set and not"auto", it loads that provider withgetSearchProvider(); ifisExplicitlyAvailable()returns true, the list is[that provider], otherwise it falls back toresolveProviderChain(authStorage, "auto"). - otherwise it calls
resolveProviderChain()with the module-global preferred provider frompackages/coding-agent/src/web/search/provider.ts.
- if
resolveProviderChain()lazily loads each provider module on demand and returns only available providers. If a preferred provider is set, it is tried first (gated byisExplicitlyAvailable()), then the staticSEARCH_PROVIDER_ORDERexcluding that provider, each gated byisAvailable(). Providers in the excluded set (setExcludedSearchProviders()) are skipped entirely, including as the preferred candidate.- If no providers are available (for example, after excluding DuckDuckGo and lacking configured keyed/OAuth providers),
executeSearch()returnsError: No web search provider configured.withdetails.response.provider = "none". - For each provider in order,
executeSearch()callsprovider.search()with:query,limit,recency,temperature,maxOutputTokens,numSearchResults,systemPromptfrompackages/coding-agent/src/prompts/system/web-search.md.
- A
SearchResponsewith no renderable content (hasRenderableSearchContent()returns false) is rejected as aSearchProviderError(status204) so the loop advances to the next provider. On the first response that has renderable content,formatForLLM()renders answer/sources/citations/related/search-queries into one text block and returns it withdetails.response. - If a provider throws,
executeSearch()records the error and tries the next provider. There is no provider-level parallel fan-out; fallback is sequential. - After all candidates fail,
formatProviderError()normalizes each error:- Anthropic
404becomesAnthropic web search returned 404 (model or endpoint not found). 401/403become<Provider> authorization failed ...except Z.AI, which preserves its raw message.- other
SearchProviderErrors surfaceerror.message.
- Anthropic
- If more than one provider was attempted, the final message is
All web search providers failed: <provider/error>; ...; otherwise it is just the normalized last error.
Modes / Variants
- Provider selection
- Forced provider: internal callers may pass
provider; unavailable forced providers fall back to the auto chain instead of hard-failing (packages/coding-agent/src/web/search/index.ts). This field is not in the model-facing schema. - Preferred provider:
setPreferredSearchProvider()sets a module-global default used byresolveProviderChain().packages/coding-agent/src/sdk.tsandpackages/coding-agent/src/modes/controllers/selector-controller.tswire this from settings. - Excluded providers:
setExcludedSearchProviders()records providersresolveProviderChain()must never return, including as fallbacks. Wired from theproviders.webSearchExcludesetting (providers.webSearchdrives the preferred provider) inpackages/coding-agent/src/sdk.ts,packages/coding-agent/src/modes/interactive-mode.ts, andpackages/coding-agent/src/modes/controllers/selector-controller.ts. - Auto chain order (25 providers):
perplexity,gemini,anthropic,codex,xai,zai,exa,tinyfish,jina,kagi,tavily,firecrawl,brave,kimi,parallel,synthetic,searxng,duckduckgo,bing,yahoo,startpage,google,ecosia,mojeek,public(SEARCH_PROVIDER_ORDERinpackages/coding-agent/src/web/search/types.ts).publicis explicit-only: itsisAvailable()returnsfalseso the auto chain never fans out implicitly.
- Forced provider: internal callers may pass
- Provider adapters
- Perplexity —
packages/coding-agent/src/web/search/providers/perplexity.ts- Availability: auth precedence is
PERPLEXITY_COOKIES-> OAuth token inagent.db->PERPLEXITY_API_KEY/PPLX_API_KEY-> anonymous ask-endpoint fallback.isAvailable()gates the auto chain on credentials, butisExplicitlyAvailable()is always true, so explicit selection works unauthenticated. - OAuth/cookie/anonymous mode: POSTs to
https://www.perplexity.ai/rest/sse/perplexity_ask, consumes SSE, merges partial events, extracts answer and source URLs, setsauthMode: "oauth"("anonymous"for the unauthenticated fallback). - API-key mode: POSTs to
https://api.perplexity.ai/chat/completionswithmodel: "sonar-pro",search_mode: "web",num_search_results, optionalsearch_recency_filter,max_tokens,temperature. num_search_resultscontrols upstream API breadth only in API-key mode.limitis preserved separately asnum_resultsand slices returnedsourcesafter parsing in both auth modes.- Output may include
answer,sources,citations,usage,model,requestId,authMode.
- Availability: auth precedence is
- Gemini —
packages/coding-agent/src/web/search/providers/gemini.ts- Availability: OAuth credentials in
agent.dbforgoogle-gemini-cli/google-antigravity, or a Google Developer API key. - Querying: SSE
streamGenerateContentcall with Google Search grounding enabled. Antigravity auth tries two fallback endpoints and retries401/403/400 invalid authonce after token refresh;429/5xxretry with exponential backoff and server-provided retry delay, capped by a5 * 60 * 1000ms rate-limit budget. - Model:
providers.webSearchGeminiModelselects the Gemini grounding model;GEMINI_SEARCH_MODELoverrides it. Defaults togemini-2.5-flash. max_tokensandtemperaturepass through asgenerationConfig.maxOutputTokens/generationConfig.temperature.limitandnum_search_resultsare collapsed together before dispatch.- Output may include
answer,sources,citations,searchQueries,usage,model.
- Availability: OAuth credentials in
- Anthropic —
packages/coding-agent/src/web/search/providers/anthropic.ts- Availability:
ANTHROPIC_SEARCH_API_KEYenv var, otherwiseauthStorage.hasAuth("anthropic"); search credentials come fromauthStorage.getApiKey("anthropic")when no search-specific key is set. - Env overrides specific to search (do not affect chat completions):
ANTHROPIC_SEARCH_API_KEY— highest-priority search auth; overridesANTHROPIC_API_KEY/ OAuth /ANTHROPIC_FOUNDRY_API_KEYfor the search call only.ANTHROPIC_SEARCH_BASE_URL— search-only base URL for eitherANTHROPIC_SEARCH_API_KEYor fallback Anthropic credentials; overridesANTHROPIC_BASE_URL(andFOUNDRY_BASE_URLin Foundry mode); defaults tohttps://api.anthropic.com.ANTHROPIC_SEARCH_MODEL— search model; defaults toclaude-haiku-4-5.
- Querying: Claude Messages API with web-search tool enabled.
max_tokensandtemperaturepass through.limitandnum_search_resultsare collapsed together before dispatch:num_results = params.numSearchResults ?? params.limit.- Output may include
answer,sources,citations,searchQueries,usage.searchRequests,model,requestId.
- Availability:
- Codex —
packages/coding-agent/src/web/search/providers/codex.ts- Availability: OAuth credential for
openai-codexinagent.db(hasOAuth(); expiry is not checked here — refresh is lazy insearchCodex). - Querying: SSE POST to
https://chatgpt.com/backend-api/codex/responseswithtool_choice: { type: "web_search" }andsearch_context_size: "high"by default. - Ignores
recency,max_tokens, andtemperaturein this tool path. limitandnum_search_resultsare collapsed together before dispatch.- Output may include
answer,sources,usage,model,requestId. If the streamed response has nourl_citationannotations, the adapter falls back to scraping markdown links and bare URLs from the answer text.
- Availability: OAuth credential for
- xAI —
packages/coding-agent/src/web/search/providers/xai.ts- Availability:
XAI_API_KEYoragent.dbcredential forxai. - Querying: POST
https://api.x.ai/v1/responseswith modelgrok-4.3andtools: [{ type: "web_search" }]using the/v1/responsesAgent Tools API. max_tokensandtemperaturepass through.recencyis sent assearch_parameters.from_date/to_date;num_search_results(orlimitwhen absent) is sent assearch_parameters.max_search_results. Because xAI citations may include every encountered URL, the adapter also locally caps returnedsourcesandcitationsafter parsing. The local cap usesnum_search_resultsbeforelimit, defaults to10when omitted/invalid/zero, and is capped at30.- Output may include
answer,sources,citations,usage,model,requestId,authMode: "api_key".
- Availability:
- Z.AI —
packages/coding-agent/src/web/search/providers/zai.ts- Availability: env or
agent.dbcredential forzai. - Querying: JSON-RPC
tools/callagainsthttps://api.z.ai/api/mcp/web_search_prime/mcpfor remote MCP toolweb_search_prime. - Fallback chain inside the provider: tries
{query,count}, then{search_query,count}, then{search_query, search_engine:"search-prime", count}when earlier attempts fail with argument-shape errors. limitandnum_search_resultsare collapsed together before dispatch.- Output may include parsed free-text
answer,sources,requestId.
- Availability: env or
- Exa —
packages/coding-agent/src/web/search/providers/exa.ts- Availability: env or
agent.dbcredential forexaadmits Exa to the auto chain; settings must not explicitly disableexa.enabledorexa.enableSearch. Explicit selection (providers.webSearch: exa) reaches Exa even without a credential and falls back to public MCP. - Querying: POST
https://api.exa.ai/searchwith the resolved Exa API key, otherwise JSON-RPCtools/callagainsthttps://mcp.exa.ai/mcpfor remote MCP toolweb_search_exa. limitandnum_search_resultsare collapsed together before dispatch.- Output: synthesized
answerfrom up to 3 result summaries,sources,requestId.
- Availability: env or
- TinyFish —
packages/coding-agent/src/web/search/providers/tinyfish.ts- Availability:
TINYFISH_API_KEYoragent.dbcredential fortinyfish. - Querying: GET
https://api.search.tinyfish.aiwithX-API-Keyandquery;recencymaps torecency_minutes. limit/num_search_results: collapsed asparams.numSearchResults ?? params.limit, clamped to1..20, default10. TinyFish has no count parameter and returns at most 10 results per page; for counts above the first page, the adapter fetches documentedpagevalues (0, then1when needed) before slicing locally. Outputsources,authMode: "api_key".
- Availability:
- Jina —
packages/coding-agent/src/web/search/providers/jina.ts- Availability:
JINA_API_KEYonly. - Querying: GET-like fetch to
https://s.jina.ai/<encoded query>with bearer auth. - Ignores
recency,max_tokens, andtemperature. limit/num_search_results: adapter slices sources toparams.numSearchResults ?? params.limitwhen provided; otherwise returns all payload items.- Output:
sourcesonly.
- Availability:
- Kagi —
packages/coding-agent/src/web/search/providers/kagi.ts,packages/coding-agent/src/web/kagi.ts- Availability: env or
agent.dbcredential forkagi. - Querying: POST
https://kagi.com/api/v1/searchwithAuthorization: Bearer <key>and JSON body{ query, workflow: "search", limit, filters?: { after } }.recencymaps tofilters.afteras a UTCYYYY-MM-DDstring (day/week/month/year). limitandnum_search_resultsare collapsed together before dispatch, clamped to1..40, default10.- Output:
sources(concatenateddata.search+data.video+data.news+data.infobox, with video/news/infobox results tagged in the title),relatedQuestions(data.adjacent_question+data.related_searchprops.question),answer(data.direct_answer[0].snippet ?? title),requestId(meta.trace).
- Availability: env or
- Tavily —
packages/coding-agent/src/web/search/providers/tavily.ts- Availability: API key from env or
agent.dbviafindCredential(). - Querying: POST
https://api.tavily.com/search. recencymaps to Tavilytime_range; code explicitly keepstopicat default general scope instead of narrowing to news.limit/num_search_results: adapter usesparams.numSearchResults ?? params.limit, clamped to5..20with default5.- Output:
answer,sources,requestId,authMode: "api_key".
- Availability: API key from env or
- Firecrawl —
packages/coding-agent/src/web/search/providers/firecrawl.ts- Availability:
FIRECRAWL_API_KEYoragent.dbcredential forfirecrawl. - Querying: POST
https://api.firecrawl.dev/v2/searchwithsources: [{ type: "web" }];recencymaps to Google-styletbs. limit/num_search_results: collapsed and clamped to1..100, default10; outputsources,requestId,authMode: "api_key".
- Availability:
- Brave —
packages/coding-agent/src/web/search/providers/brave.ts- Availability:
BRAVE_API_KEYonly. - Querying: GET
https://api.search.brave.com/res/v1/web/searchwithcount,extra_snippets=true, andfreshness=pd|pw|pm|pyforrecency. limit/num_search_results:params.numSearchResults ?? params.limit, clamped to1..20, default10.- Output:
sources,requestId.
- Availability:
- Kimi —
packages/coding-agent/src/web/search/providers/kimi.ts- Availability:
MOONSHOT_SEARCH_API_KEY,KIMI_SEARCH_API_KEY,MOONSHOT_API_KEY, oragent.dbcredentials formoonshot/kimi-code. - Querying: POST to
MOONSHOT_SEARCH_BASE_URL/KIMI_SEARCH_BASE_URL/ defaulthttps://api.kimi.com/coding/v1/searchwithtext_query,limit,enable_page_crawling,timeout_seconds: 30. limit/num_search_results:params.numSearchResults ?? params.limit, clamped to1..20, default10.- Output:
sources,requestId.
- Availability:
- Parallel —
packages/coding-agent/src/web/search/providers/parallel.ts,packages/coding-agent/src/web/parallel.ts- Availability: env or
agent.dbcredential forparallel. - Querying: POST
https://api.parallel.ai/v1beta/searchwithobjective=query,search_queries=[query],mode:"fast",max_chars_per_result: 10000, beta headersearch-extract-2025-10-10. - There is no provider fan-out here despite the name; the current adapter always sends a one-element
search_queriesarray. limitandnum_search_resultsare collapsed together before dispatch, clamped to1..40, default10.- Output:
sources,requestId.
- Availability: env or
- Synthetic —
packages/coding-agent/src/web/search/providers/synthetic.ts- Availability: env or
agent.dbcredential forsynthetic. - Querying: POST
https://api.synthetic.new/v2/searchwith{ query }. - Ignores
recency,max_tokens, andtemperature. limitandnum_search_resultsare collapsed together before dispatch.- Output:
sourcesonly.
- Availability: env or
- SearXNG —
packages/coding-agent/src/web/search/providers/searxng.ts- Availability: endpoint from
searxng.endpointsetting orSEARXNG_ENDPOINTenv. - Querying: GET
<endpoint>/search?format=json&q=...; optional settings addcategoriesandlanguage. - Auth precedence: Basic auth (
searxng.basicUsername/searxng.basicPasswordor env equivalents) over bearer token (searxng.token/SEARXNG_TOKEN). Basic credentials are validated for RFC 7617 restrictions. recencymaps totime_range;weekis downgraded tomonthbecause SearXNG does not support week.limitandnum_search_resultsare collapsed together before dispatch, clamped to1..20, default10.- Output:
sources,relatedQuestionsfromsuggestions.
- Availability: endpoint from
- DuckDuckGo —
packages/coding-agent/src/web/search/providers/duckduckgo.ts- Availability: always available; no API key.
- Querying: POST the no-JS HTML frontend
https://html.duckduckgo.com/html/withq,kl=us-en, and an optionaldfrecency filter (d/w/m/y); parses the result list and unwraps//duckduckgo.com/l/?uddg=…redirect URLs. recencymaps todf; values outsideday|week|month|yearare ignored.limit/num_search_results: collapsed and clamped to1..20, default10; output exposessourcesonly (DuckDuckGo's HTML page does not return a standalone abstract).- DuckDuckGo serves a bot-detection challenge (HTTP 200/202 with an
anomaly-modalbody) when it throttles datacenter or shared-egress IPs. The adapter detects this and raises aSearchProviderErrorso the orchestrator can fall through to the next configured provider with a clear cause.
- Bing / Yahoo / Startpage —
providers/bing.ts,providers/yahoo.ts,providers/startpage.ts- Availability: always available; no API key. Plain fetch with shared browser navigation headers.
- Bing: GET
https://www.bing.com/search; unwrapsbing.com/ck/a?...&u=a1<base64url>redirect hrefs;recencymaps tofilters=ex1:"ez1|ez2|ez3"and a computedez5epoch-day range foryear. - Yahoo: GET
https://search.yahoo.com/search; unwrapsr.search.yahoo.com/.../RU=<pct-encoded>tracker hrefs;recencymaps tobtf=d|w|m(yeardropped). - Startpage: proxies Google's index; GET homepage to lift the
scanti-bot form token, then POST/sp/search(tokenless GET fallback);recencymaps towith_date=d|w|m|y. - Each detects its engine's bot-challenge/consent page and raises a provider-tagged
SearchProviderError(429) so the chain advances.
- Google / Ecosia / Mojeek —
providers/google.ts,providers/ecosia.ts,providers/mojeek.ts- Availability: always available; no API key.
browserFetch(providers/browser-page.ts) tries a browser-profiled plain fetch first and escalates fetch failures, non-2xx statuses, and challenge bodies to the shared stealth headless browser (acquireBrowser); an injectedparams.fetch(tests) never escalates. - Google: seeds cookies via the homepage, then loads the rendered SERP;
recencymaps totbs=qdr:*. Ecosia sits behind Cloudflare (hence the browser); its organic results are Google-backed;recencyis a server-side no-op and silently ignored. Mojeek fronts an ALTCHA proof-of-work wall that the browser path auto-solves;recencymaps tosince=day|week|month|year. - Challenge pages (Google
unusual traffic, Ecosia Firewall, Mojeek ALTCHA/robot 403) raise provider-taggedSearchProviderErrors (429).
- Availability: always available; no API key.
- Public Web —
packages/coding-agent/src/web/search/providers/public.ts- Availability: explicit selection only (
isAvailable()isfalse;isExplicitlyAvailable()istrue). - Querying: fans out to every credential-free engine in parallel (
duckduckgo,bing,yahoo,startpage,google,ecosia,mojeek, minus excluded ones), then consolidates: URLs deduplicated on a canonical key (host withoutwww., no trailing slash, no fragment), ranked by cross-engine consensus, then best per-engine rank; the longest snippet wins. - Deadline race: returns at the earliest of all engines settled, 5s soft deadline with at least one success, or 30s hard cap; stragglers are aborted. Individual engine failures are tolerated; it fails only when every engine fails (aggregated 503).
- Availability: explicit selection only (
- Perplexity —
Side Effects
- Network
- Calls one or more external search providers over HTTPS until one succeeds or all fail.
- Provider-specific transports include JSON POST, JSON GET, SSE streaming (Perplexity OAuth/API, Gemini, Codex), and JSON-RPC over HTTP (Z.AI).
- Subprocesses / native bindings
- None.
- Session state (transcript, memory, jobs, checkpoints, registries)
- Uses a module-global provider-instance cache in
packages/coding-agent/src/web/search/provider.ts. - Uses a module-global preferred-provider setting in the same file.
packages/coding-agent/src/tools/index.tsgates tool availability behindsession.settings.get("web_search.enabled").
- Uses a module-global provider-instance cache in
- Background work / cancellation
- Many provider adapters accept
AbortSignal;WebSearchTool.execute()passes the tool call signal intoexecuteSearch(), which forwards it asparams.signalto providers and rethrows cancellation during fallback.
- Many provider adapters accept
Limits & Caps
- Provider auto-order length: 25 providers (
SEARCH_PROVIDER_ORDERinpackages/coding-agent/src/web/search/types.ts). formatForLLM()truncates source snippets and citation text to 240 chars (packages/coding-agent/src/web/search/index.ts).formatForLLM()emits at most 3 search queries, each truncated to 120 chars (packages/coding-agent/src/web/search/index.ts).- Brave result count: default
10, max20(DEFAULT_NUM_RESULTS,MAX_NUM_RESULTSinpackages/coding-agent/src/web/search/providers/brave.ts). - TinyFish local result count: default
10, max20; the API has no count parameter and returns at most 10 results per page, so the adapter fetches documented pages (page=0, thenpage=1when needed) and slices locally (packages/coding-agent/src/web/search/providers/tinyfish.ts). - DuckDuckGo result count: default
10, max20(packages/coding-agent/src/web/search/providers/duckduckgo.ts). - Bing / Yahoo / Startpage / Google / Ecosia / Mojeek result count: default
10, max20(theirproviders/*.tsmodules). - Public Web result count: default
15, max30; fan-out soft deadline5s, hard cap30s(packages/coding-agent/src/web/search/providers/public.ts). - Tavily result count: default
5, max20(packages/coding-agent/src/web/search/providers/tavily.ts). - Firecrawl result count: default
10, max100(packages/coding-agent/src/web/search/providers/firecrawl.ts). - Kimi result count: default
10, max20; request timeout field fixed to30seconds (packages/coding-agent/src/web/search/providers/kimi.ts). - Parallel result count: default
10, max40; per-result excerpt cap10_000chars (packages/coding-agent/src/web/search/providers/parallel.ts,packages/coding-agent/src/web/parallel.ts). - Kagi result count: default
10, max40(packages/coding-agent/src/web/search/providers/kagi.ts). - SearXNG result count: default
10, max20(packages/coding-agent/src/web/search/providers/searxng.ts). - xAI local sources/citations cap and upstream
max_search_results:num_search_resultsbeforelimit, omitted/invalid/zero => local default10, max30(packages/coding-agent/src/web/search/providers/xai.ts). - Perplexity API-key mode defaults:
max_tokens = 8192,temperature = 0.2,num_search_results = 20(packages/coding-agent/src/web/search/providers/perplexity.ts). - Anthropic defaults: model
claude-haiku-4-5,DEFAULT_MAX_TOKENS = 4096when the provider omitsmax_tokens(packages/coding-agent/src/web/search/providers/anthropic.ts). - Gemini retries: up to
3retries per endpoint, base delay1000ms, rate-limit delay budget5 * 60 * 1000ms (packages/coding-agent/src/web/search/providers/gemini.ts).
Errors
- Tool-level no-provider case returns a normal tool result with
Error: No web search provider configured.; it does not throw. - Tool-level all-failed case also returns a normal tool result with
Error: ...; the message is either the single normalized provider error or a semicolon-separated summary of all failed providers. - Provider adapters usually throw
SearchProviderError(provider, message, status)for HTTP or protocol failures. - Availability probes intentionally swallow lookup errors and report
falsein many providers viaisApiKeyAvailable(). - Per-provider notable failures:
- Anthropic: missing credentials throw a plain
Error; a404is remapped to a special final message byformatProviderError(). - Perplexity: missing auth throws a plain
Error; OAuth streamerror_codeevents becomeSearchProviderError("perplexity", ...). - Gemini: auth refresh, endpoint fallback, and retry logic are internal; final exhausted failures surface as
SearchProviderError("gemini", ...). - Codex and Gemini both fail if the HTTP response has no body after a
200. - Z.AI treats malformed SSE/JSON-RPC payloads as provider errors and retries only argument-shape failures across request variants.
- SearXNG
findAuth()can throw configuration errors before any HTTP call if Basic auth fields are incomplete or invalid.
- Anthropic: missing credentials throw a plain
Notes
- The model-facing schema does not expose
provider, but internal callers can force one throughSearchQueryParams. resolveProviderChain()lazily imports provider modules and caches singleton instances. Just asking for labels viagetSearchProviderLabel()does not trigger those imports.- Most providers treat
limitandnum_search_resultsas the same number because adapters passparams.numSearchResults ?? params.limit. Perplexity preserves both concepts. TinyFish uses the collapsed value as a local cap, serializesnum_resultsper page, and paginates withpagewhen more results are needed. xAI sends that collapsed value assearch_parameters.max_search_resultsand applies the same precedence locally after parsing to cap returned sources/citations (10default,30max). recencyis implemented by Brave, Perplexity, Tavily, SearXNG, Kagi, TinyFish, Firecrawl, xAI, DuckDuckGo, Bing, Yahoo, Startpage, Google, and Mojeek (Ecosia ignores it; Public Web passes it through). The model-facing prompt does not name specific providers.packages/coding-agent/src/config/settings-schema.tsuses the sharedSEARCH_PROVIDER_PREFERENCES/SEARCH_PROVIDER_OPTIONSmetadata, so the settings selector and setup wizard exposeautoplus every provider in the auto chain.- The credential-free scrapers close the auto chain, cheap plain-fetch engines first (
duckduckgo,bing,yahoo,startpage) and browser-backed ones after (google,ecosia,mojeek);publicis listed last and never auto-selected. - Exa uses
authStorage.getApiKey("exa"), thenEXA_API_KEY, then unauthenticatedhttps://mcp.exa.ai/mcpfallback.