Overview
dsh-web-search-exa
WebSearchProvider for the harness web capability seam (ctx.web). It calls Exa's POST /search endpoint with highlight contents and maps the flat results[] into the seam's normalized WebSearchResult.This is an atomic module already shipped with Harness, not a standalone profile layer.
Capabilities
What it contributes
README / EN
Package documentation
@deepseek-ai/dsh-web-search-exa
English | 中文
An Exa-backed WebSearchProvider for the harness web capability seam (ctx.web). It calls Exa's POST /search endpoint with highlight contents and maps the flat results[] into the seam's normalized WebSearchResult.
This is an implementation package: it registers a provider into ctx.web, it does not own the ctx.web key and it does not register a model-facing tool (that is @deepseek-ai/dsh-tool-web). Like @deepseek-ai/dsh-llm-deepseek, it is a function/namespace plugin (inject: ['web']) that registers its backend, not a default-export service.
Config
| Key | Default | Meaning |
|---|---|---|
apiKey |
$EXA_API_KEY |
Exa API key. Empty/absent makes the provider unavailable. |
baseURL |
https://api.exa.ai |
Endpoint base; /search is appended. An unparseable value makes the provider unavailable. |
searchType |
auto |
Retrieval mode sent as Exa's type: auto (Exa decides), keyword, or neural. |
numResults |
(unset) | Default result count when a request carries no maxResults. Unset sends no default. Must be a positive integer. |
highlightsPerResult |
1 |
Highlight sentences requested per result (Exa's highlightsPerUrl). Must be a positive integer. |
- id: web-search-exa
name: '@deepseek-ai/dsh-web-search-exa'
config:
apiKey: !!js process.env.EXA_API_KEY
Mapping
Exa returns a flat results[] and no generated answer, so content is omitted. Each result maps to a WebSearchSource: url ← url, title ← title, snippet ← the first non-empty highlights[] entry (a result with no highlight has no portable snippet and is dropped), publishedAt ← publishedDate. A request's maxResults wins over the configured numResults default and is sent as Exa's numResults for a cost/latency optimization; the final bound is enforced by the seam. Provider failures (HTTP errors, network failure, unparseable or wrong-shape bodies) surface as WebError WEB_PROVIDER_ERROR; an aborted request surfaces as WEB_ABORTED. HTTP redirects are rejected before the Location target is contacted and surface as WEB_PROVIDER_ERROR.
Model Experience
Indirectly, through dsh-tool-web, which retains this provider's maxResults-bounded URLs, titles, first highlights, and publication dates or its exact Exa search aborted, Exa search request failed: <error>, and Exa returned an unprocessable response body: <error> failures under the consumer's error wrapper while generated answers and provider-private fields remain outside context.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Known Limitations and Deferred Work
- A result with no non-blank highlight is dropped entirely — no portable snippet to map, so fewer sources than the requested count can return.
- Only
searchType/numResults/highlightsPerResultare exposed — Exa's other controls (livecrawl, category, domain/date filters, full-text contents) wait on provider-neutral Service Definition fields (seam Agent Note). - Abort classification is error-shape-based — only a
DOMExceptionnamedAbortErrormaps toWEB_ABORTED; an abort carrying a custom reason (e.g.dsh-timeout'sTimeoutReason) surfaces asWEB_PROVIDER_ERROR.
LIMITATIONS
Known limitations
- **A result with no non-blank highlight is dropped entirely** — no portable snippet to map, so fewer sources than the requested count can return. - **Only `searchType`/`numResults`/`highlightsPerResult` are exposed** — Exa's other controls (livecrawl, category, domain/date filters, full-text contents) wait on provider-neutral Service Definition fields ([seam Agent Note](../../../.agents/notes/implemented/architecture/2026-06-24-web-capability-seam.md)). - **Abort classification is error-shape-based** — only a `DOMException` named `AbortError` maps to `WEB_ABORTED`; an abort carrying a custom reason (e.g. `dsh-timeout`'s `TimeoutReason`) surfaces as `WEB_PROVIDER_ERROR`.
