* fix: custom web search — WEB_URL_TEMPLATE not recognized, timeout too short, silent native fallback
1. custom.ts: Add WEB_URL_TEMPLATE to isConfigured() so the custom provider
is recognized when configured via URL template alone.
2. custom.ts: Bump DEFAULT_TIMEOUT_SECONDS from 15s to 120s.
Self-hosted search APIs (SearXNG, internal) commonly need 30-90s.
3. WebSearchTool.ts: When an explicit adapter is selected via
WEB_SEARCH_PROVIDER=custom, do not silently fall through to the
native Anthropic path on adapter errors or 0-hit results.
- 0 hits: return directly (no fallback)
- Error: throw the real error (no fallback)
- Auto mode: existing fallback behavior preserved
* fix: tighten auto-mode adapter fallback — only swallow transient errors
Address review feedback: in auto mode, only fall through to native on
transient errors (network failure, timeout, HTTP 5xx). Config and
guardrail errors (SSRF, HTTPS, bad URL, header allowlist, etc.) now
surface properly instead of being silently swallowed.
---------
Co-authored-by: FluxLuFFy <fluxluffy@users.noreply.github.com>
* refactor: provider adapter system + 7 new search providers
Architecture:
- Each search backend is a small adapter implementing SearchProvider
- 12 providers: custom, tavily, exa, you, jina, bing, mojeek, linkup, firecrawl, duckduckgo + native
- WEB_SEARCH_PROVIDER controls selection: auto (fallback chain) or specific provider
- Auth always in headers, never in query strings
Bug fixes from review feedback:
- Fix applyDomainFilters catch block: keep hits with malformed URLs on blocked_domains
(can't confirm blocked), drop on allowed_domains (can't confirm allowed)
- Add safeHostname() helper: safely extract hostname from URLs without throwing
- Replace unsafe new URL(r.url).hostname in 7 providers with safeHostname()
- Remove dead code: buildAllHeaders, buildAuthHeaders, parseExtraHeaders from types.ts
- Fix WEB_PARMS typo: consistently use WEB_QUERY_PARAM everywhere
- AbortSignal forwarded to fetch() in all 12 providers
- DuckDuckGo: wrap dynamic import in try/catch for graceful error
- Exa: remove double domain filtering (server-side already)
- runSearch(): aggregate all provider errors instead of throwing only the last one
- Retry logic: check numeric status code directly, retry 5xx/network, skip 4xx
Test coverage (44 tests, all passing):
- types.test.ts: safeHostname, normalizeHit, applyDomainFilters (20 tests)
- index.test.ts: getProviderMode, getProviderChain, getAvailableProviders (13 tests)
- custom.test.ts: extractHits flexible response parsing (11 tests)
Co-authored-by: FluxLuFFy <195792511+FluxLuFFy@users.noreply.github.com>
* security: add guardrails to custom search provider (Option B)
- HTTPS-only by default (opt-out: WEB_CUSTOM_ALLOW_HTTP=true)
- Private/localhost IPs blocked by default (opt-out: WEB_CUSTOM_ALLOW_PRIVATE=true)
- Header allowlist: only known-safe headers allowed unless WEB_CUSTOM_ALLOW_ARBITRARY_HEADERS=true
- Configurable timeout in seconds (WEB_CUSTOM_TIMEOUT_SEC, default 15)
- Configurable POST body limit (WEB_CUSTOM_MAX_BODY_KB, default 300)
- Removed max URL size restriction
- Audit log warning on first custom search call
- Updated .env.example and README_SEARCH_PROVIDERS.md with all new options
* fix: remove custom provider from auto chain (Option 1)
Remove customProvider from the auto fallback chain so it is only
available when WEB_SEARCH_PROVIDER=custom is explicitly selected.
Changes:
- Remove customProvider from ALL_PROVIDERS array in providers/index.ts
- Add 3 new tests verifying custom is excluded from auto chain
- Update README_SEARCH_PROVIDERS.md: auto priority, mode table, note
- Update .env.example: auto priority comment, custom mode annotation
All 47 tests pass (44 existing + 3 new).
Co-Authored-By: @Vasanthdev2004
* fix: address review blockers (routing, abort, config check, domain matching)
1. Native/Codex routing precedence in auto mode
shouldUseAdapterProvider() now checks if native/first-party/vertex/foundry
or Codex paths are available before falling back to adapter providers.
Auto mode: native paths take precedence; adapter is fallback only.
2. AbortError stops provider chain immediately
runSearch() now checks for AbortError/aborted signal before continuing
the fallback chain. Cancelled searches don't create extra outbound requests.
3. Explicit provider mode fails fast on missing credentials
runSearch() validates isConfigured() for explicit modes before attempting
requests. Throws clear error: 'Search provider "X" is not configured.'
4. Domain filter exact-or-subdomain matching (fixes suffix collision)
New hostMatchesDomain() helper: exact match or .subdomain match.
badexample.com no longer matches example.com.
5. Tests: 56 pass (9 new) covering all 4 fixes
Co-Authored-By: @Vasanthdev2004
---------
Co-authored-by: Claude Fix <fix@openclaude.local>
Co-authored-by: FluxLuFFy <195792511+FluxLuFFy@users.noreply.github.com>
Co-authored-by: bot <bot@openclaw.ai>