Provider-backed web_search tool for pi. Activation is explicit: if no config is found, the extension stays inactive and surfaces a TUI startup error.
The public tool schema matches free-code:
{
"query": "The search query to use",
"allowed_domains": ["example.com"],
"blocked_domains": ["spam.example.com"]
}Only query is required. allowed_domains and blocked_domains are mutually exclusive for a single tool call. Result limits are configured per provider, not exposed in the tool schema.
The loader checks these files in order:
.pi/websearch.jsonin the current project~/websearch.json~/.pi/websearch.json
Example:
{
"provider": "perplexity",
"apiKey": "pplx-...",
"maxResults": 8,
"allowedDomains": ["docs.example.com"]
}Multiple provider entries enable fallback and routing:
{
"strategy": "priority",
"fallback": true,
"auto": true,
"providers": [
{
"id": "brave-search",
"provider": "brave",
"apiKey": "<local-only-key>",
"priority": 10,
"maxResults": 8
},
{
"id": "exa-search",
"provider": "exa",
"apiKey": "<local-only-key>",
"priority": 10,
"maxResults": 8
}
]
}When auto is true (the default) and the active pi model exposes a server-hosted search tool, the extension prepends an implicit { id: "native", ... } entry that reuses the model's resolved API key via ExtensionContext.modelRegistry.getApiKeyAndHeaders. The native entry tries first; on failure or when the model does not match, the configured providers handle the search. Disable with "auto": false if you want only the explicit providers list.
Models that activate native routing (Q1 2026):
openai:gpt-5.5,gpt-5.5-fast,gpt-4.1,gpt-4.1-mini,gpt-4o,gpt-4o-mini(excludesgpt-4.1-nano,gpt-5-codex,gpt-5.1-codex).anthropic:claude-opus-4-*,claude-sonnet-4-*.xai: anygrok-*.perplexity: anysonar*(search is intrinsic to Sonar models).z-aiorzai: anyglm-*.openrouter: any<provider>/<model>whose<provider>and<model>match one of the rows above (for exampleopenai/gpt-5.5oranthropic/claude-opus-4-7).
The native entry inherits model.baseUrl from ExtensionContext.model, so any local gateway override registered in the pi model registry is honored. The endpoint path is appended automatically: if baseUrl already ends with /v1, only the resource segment is added; otherwise /v1/<resource> is appended.
Routing strategies:
priority: try lowerpriorityvalues first, falling back in order whenfallbackistrue.round-robin: rotate the first provider per search; optionalweightrepeats entries in the rotation.fill-first: collect unique results across providers until the requested result count is filled.
Supported providers:
exa: direct Exa search. RequiresapiKey.tavily: direct Tavily search. RequiresapiKey.brave: Brave Search API. RequiresapiKey.serper: Serper Google search API. RequiresapiKey.google-cse: Google Custom Search JSON API. RequiresapiKeyandsearchEngineId.z-ai: Z.ai web search endpoint. RequiresapiKey.openai: OpenAI Responses API hostedweb_searchtool. RequiresapiKey.codex: OpenAI Responses API hostedweb_searchtool. RequiresapiKey.anthropic: Anthropic Messages API with serverweb_search_20250305tool. RequiresapiKey.perplexity: Perplexity Search API. RequiresapiKey.xai: xAI Responses API hostedweb_searchtool. RequiresapiKey.
Provider-specific optional fields include id, baseUrl, model, maxResults, priority, weight, searchContextSize, codexMode, allowedDomains, blockedDomains, and userLocation where supported. baseUrl is supported for every provider and must be a public HTTPS URL without embedded credentials.
Tool text and TUI output include the selected provider entry, routing strategy, and fallback attempts so the agent can see which provider produced the result.
npm test
npm run typecheck
npm run check
pi -e ./src/index.tsInside pi, run /websearch status to inspect activation state.
- senpi — the fork/runtime these extensions are extracted from.
- Ultraworkers Discord — community link from the senpi README.
- Dori — the product powered by senpi under the hood.