#13370: Tools: rewrite Grok parser, add Tavily provider, multi-provider configure wizard
commands
agents
stale
size: XL
Cluster:
Web Search Provider Enhancements
#### Summary
Rewrites the Grok web search parser, adds Tavily as a new provider, refactors `runWebSearch` into a clean provider-dispatch architecture, and updates the configure wizard to support all four web search providers with a uniform config layout.
Closes #13171. Supersedes #13263.
lobster-biscuit
#### Behavior Changes
- **Grok web_search now works correctly** — previous parser failed to extract text/citations from the xAI Responses API format (`type: "message"` + `status: "completed"`)
- **Tavily available as a new web_search provider** — configurable via `tools.web.search.provider: "tavily"` with API key via config or `TAVILY_API_KEY` env var; supports `basic`, `advanced`, `fast`, and `ultra-fast` search depths
- **Configure wizard supports all 4 providers** — `openclaw configure --section web` now lets users pick Brave, Perplexity, Grok, or Tavily and enter per-provider API keys (previously Brave-only)
- **Brave API key normalized** — moved from `tools.web.search.apiKey` to `tools.web.search.brave.apiKey` so all providers follow the same `[provider].apiKey` pattern; legacy migration auto-moves existing configs
- **`GrokSearchResult.output` is now optional** — handles malformed/edge API responses without crashing
- **Dead `inlineCitations` config field removed** from Grok config
- **Onboarding summary is provider-agnostic** — no longer mentions only Brave Search
No changes to Brave or Perplexity provider runtime behavior.
#### Codebase and GitHub Search
- Searched for existing Tavily integrations — none found; this is the first Tavily provider
- Verified `performProviderSearch` dispatch covers all four providers with `default: throw` exhaustiveness guard
- Confirmed `buildSearchCacheKey` produces unique keys per provider (covered by tests)
- Reviewed Greptile feedback on superseded #13263 — both flagged issues (citation URL wrapping, dead `inlineCitations` config) resolved here
- Searched all references to `search.apiKey` / `search?.apiKey` to ensure no stale paths remain after the Brave key normalization
#### Tests
- Split `web-search.test.ts` into focused per-provider files:
- `web-search.test.ts` — shared (perplexity, freshness, cache key, provider dispatch)
- `web-search.brave.test.ts` — Brave search tests
- `web-search.grok.test.ts` — Grok config, fixtures, error handling
- `web-search.tavily.test.ts` — Tavily config, fixtures, error handling
- Fixture-driven tests for Grok and Tavily response parsing
- Updated `config.env-vars.test.ts` to use new `brave.apiKey` path
- All existing tests pass:
- `pnpm vitest run src/agents/tools/web-search` — 81 tests pass (4 files)
- `pnpm vitest run src/commands/configure.wizard.test.ts` — 1 test pass
- `pnpm vitest run src/config/config.web-search-provider.test.ts` — 2 tests pass
- `pnpm vitest run src/config/config.legacy-config-detection` — 57 tests pass
- `pnpm vitest run src/config/config.env-vars.test.ts` — 4 tests pass
- `pnpm build && pnpm check` — clean
#### Manual Testing
##### Prerequisites
- xAI API key (`XAI_API_KEY`)
- Tavily API key (`TAVILY_API_KEY`)
##### Steps
1. Configure Grok provider (`tools.web.search.provider: "grok"`) and run a web search query — verify results contain text content and citation URLs
2. Configure Tavily provider (`tools.web.search.provider: "tavily"`) and run a web search query — verify results contain titles, URLs, and descriptions
3. Verify Brave search still works with no config changes (default provider)
4. Run `openclaw configure --section web` — verify provider selection (Brave/Perplexity/Grok/Tavily), per-provider API key prompt, and correct env var hints
5. Place `tools.web.search.apiKey: "BSA-test"` in config, reload — verify legacy migration moves it to `tools.web.search.brave.apiKey`
6. Run `openclaw onboard` — verify the web search summary note is provider-agnostic
##### Results
- [x] Grok search via live xAI key — working
- [x] Tavily search via live key — working
- [x] Configure wizard: all 4 providers selectable, API key stored at correct path
- [x] Legacy migration: `apiKey` → `brave.apiKey` auto-migrated on load
- [x] Onboarding summary: provider-agnostic messaging
- [ ] Brave search via live key — no key available (no regression in code paths)
**Sign-Off**
- Models used: Claude Opus 4.6
- Submitter effort: high — multi-session development across parser rewrite, new provider, refactor, test reorganization, configure wizard rewrite, and config normalization with legacy migration
- Agent notes: Supersedes #13263 with expanded scope; all Greptile review findings from that PR are addressed here
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR refactors `web_search` into a provider-dispatch architecture, fixes Grok (xAI Responses API) parsing, adds a new Tavily provider, and updates config + configure wizard to support provider-specific API keys under `tools.web.search.<provider>.apiKey` (with a legacy migration for Brave’s old `tools.web.search.apiKey`).
The changes fit the existing tool/config architecture by:
- keeping `web_search` as a single tool with a stable input schema, while selecting the runtime provider from config
- centralizing cache-key generation and provider execution in `src/agents/tools/web-search.ts`
- extending the Zod runtime schema + config types and migrating legacy config keys on load.
Main issue to address before merge: Grok citation extraction currently doesn’t filter/validate annotations, which can produce invalid citation objects if the API returns non-URL annotations or missing fields.
<h3>Confidence Score: 4/5</h3>
- This PR looks close to safe to merge, with one concrete runtime issue to fix in Grok citation parsing.
- Most changes are additive/refactoring with solid test coverage and schema updates. The remaining concern is that Grok annotation-to-citation mapping doesn’t filter/validate fields, which can yield invalid citation objects and break consumers that expect valid URLs/indices. Also a minor text-file newline issue in CLAUDE.md will cause churn.
- src/agents/tools/web-search.ts, CLAUDE.md
<sub>Last reviewed commit: 4ded777</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#13386: feat(web-search): add Nimble Web Search API provider
by ilchemla · 2026-02-10
82.1%
#22505: Feature/clean grok search base url
by vacuityv · 2026-02-21
82.0%
#17632: feat(web-search): add per-call provider override
by cog-bernthiddema · 2026-02-16
81.6%
#13075: [Feature]: Add Gemini (Google Search grounding) as web_search provider
by akoscz · 2026-02-10
81.1%
#8715: fix(web-search): safer provider resolution & Perplexity auto-detection
by abhijeet117 · 2026-02-04
80.9%
#13814: feat(web-search): add ZAI Search (zsearch) provider
by strelov1 · 2026-02-11
80.6%
#6743: feat(web-search): add Tavily search provider support
by 3927o · 2026-02-02
79.6%
#11444: Add extensible search providers for web_search
by dhc02 · 2026-02-07
79.2%
#17729: fix(configure): update web tools hint to reflect all search providers
by raktim-mondol · 2026-02-16
79.1%
#8707: fix(web_search): honor configured provider at execution timeFix/web...
by codvik · 2026-02-04
78.1%