The web packages were authored against the old single-stage layout where tsc emitted directly to lib/. Master compiles declarations to lib/types/ via tsc -b, then bundles JS into lib/ via tsdown. Point every web package's tsc outDir at lib/types, update package.json types/exports/files to the lib/types declaration + lib/ bundle shape (matching dsh-bash/dsh-tool-bash), and bundle tool-web's subpath entries from lib/types/*.js rather than src.
@deepseek-ai/dsh-web-search-exa
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 → provider status() reports missing-credential (the seam reports configured-unavailable/none). |
baseURL |
https://api.exa.ai |
Endpoint base; /search is appended. An unparseable value makes status() report misconfigured. |
- 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. The provider passes maxResults through as Exa's numResults for a cost/latency optimization; the final bound is enforced by the seam. Provider failures (HTTP errors, network failure, unparseable bodies) surface as WebError WEB_PROVIDER_ERROR; an aborted request surfaces as WEB_ABORTED.