Skip to content

dsh-search-providers

Verified

@mini-z/dsh-search-providers Β· v0.3.0 Β· MIT

Modular DSH search plugin with Codex-first web search, page fetching, and capability-aware automatic failover.

Install

dsh plugin add @mini-z/dsh-search-providers

Confirm the layer applied with dsh --profile default --dump-config β€” see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Tags

Creators

Readme

@mini-z/dsh-search-providers

npm version license

A modular DeepSeek Harness (DSH) plugin that provides web_search and web_fetch through multiple search services with capability-aware fallback and cooldown handling.

Codex is the default web_search provider. If its static credential is unavailable, or a request fails, the plugin proceeds through the configured fallback chain.

Latest update β€” 0.3.0

  • Breaking: native web_fetch takeover. The plugin now supplies both native web tool backends and no longer registers read_page.
  • Remove page-reading focus parameters completely. Use web_fetch({ url }); there is no fetch query parameter or TinyFish fetch purpose forwarding. Search keywords are unchanged.
  • Extracted evidence is returned in native body.content, with explicit truncation and unknown origin status (statusCode: 0) reporting.
  • Preserve TinyFish β†’ Exa β†’ Firecrawl fetch fallback, cooldown, cancellation, and a 200-second aggregate deadline.
  • Existing JSON configuration and Codex credential formats remain supported. Update the plugin and restart DSH. See migration details and the changelog.

Upgrading from 0.1.x? Move provider settings from .env or inline Cordis configuration into ~/.dsh/search-providers.json. Official endpoints are built in, and fallback attempts now have a 55-second outer timeout. See configuration migration.

Features

  • Replaces the native web_search provider through the DSH web seam.
  • Replaces the native web_fetch provider for clean page extraction; no separate read_page tool is registered.
  • Uses Codex-first search routing by default.
  • Filters fallback candidates by capability, so search-only providers are never used for page fetching.
  • Temporarily cools failing provider capabilities instead of repeatedly calling them.
  • Disables only confirmed authentication failures until DSH restarts.
  • Propagates caller cancellation without cooling providers or continuing fallback.
  • Supports custom active-provider and fallback order configuration.
  • Includes a key-free demo provider for wiring tests.

Provider support and default routing

Provider web_search web_fetch Credential
Codex βœ… ❌ Static credential JSON referenced by providers.codex.credentialFile
TinyFish βœ… βœ… providers.tinyfish.apiKey in JSON
Tavily βœ… ❌ providers.tavily.apiKey in JSON
Exa βœ… βœ… providers.exa.apiKey in JSON
Firecrawl βœ… βœ… providers.firecrawl.apiKey in JSON
Demo βœ… βœ… None

Default web_search order:

  1. Codex
  2. TinyFish
  3. Tavily
  4. Exa
  5. Firecrawl

Default web_fetch order:

  1. TinyFish
  2. Exa
  3. Firecrawl

Codex and Tavily are skipped automatically for web_fetch because they do not implement page fetching. Providers without usable credentials are skipped without being marked as failed.

Native web_fetch migration

The bundle selects search-providers for both web.searchProvider and web.fetchProvider. DSH continues to own the native tool schemas, system guidance, and result cards; the plugin supplies the backends. It no longer registers read_page.

  • Call web_fetch({ url }). Page reading accepts no focus question: the former query field has been removed from both the tool interface and internal fetch request type, including TinyFish's fetch purpose mapping. Search query parameters are unchanged.
  • Results use the native { url, statusCode, body: { kind: 'text', content }, truncated } shape. Summary, extracted Markdown, links, uncertainty, and fallback warnings are rendered inside body.content, not separate top-level fields.
  • Extraction providers do not expose the origin HTTP status or final redirect URL. The adapter returns statusCode: 0 (unknown, not HTTP success), retains the requested URL, and includes an explicit warning. Do not use this backend for HTTP status/redirect diagnostics.
  • Content is capped at 20,000 characters and outgoing links at 20; either cap sets truncated: true. Native DSH presentation may apply an additional display cap.
  • The default fetch route is TinyFish β†’ Exa β†’ Firecrawl. There is no automatic fallback to DSH's direct HTTP provider. URLs are sent to third-party extraction services, unlike local HTTP retrieval; do not pass private or sensitive URLs.
  • Existing JSON provider settings need no changes. Install the rebuilt plugin into the profile and restart DSH; editing a separate source checkout does not update an installed npm copy.

Installation

Install from npm

npx -y @deepseek-ai/dsh plugin --profile web add @mini-z/dsh-search-providers

Replace web with another DSH profile name when needed. Restart the corresponding DSH process after installation or configuration changes.

Install from a local checkout or tarball

npx -y @deepseek-ai/dsh plugin --profile web add /path/to/dsh-search-providers

The npm package and release tarballs include the built dsh/ artifacts. A local source checkout only needs rebuilding after source changes:

bun install --frozen-lockfile
bun run build

Configuration

The independent JSON configuration loader replaces inline Cordis settings and environment-variable configuration. Cordis accepts only the optional configFile setting; when omitted, it defaults to ~/.dsh/search-providers.json.

The package-root search-providers.example.json is included in the package. Copy it to your chosen configuration path and supply credentials for at least one provider. For example, ~/.dsh/search-providers.json:

{
  "provider": "codex",
  "fallbackChain": ["tinyfish", "tavily", "exa", "firecrawl"],
  "providers": {
    "codex": {
      "credentialFile": "./codex-credential.json",
      "timeoutMs": 55000
    },
    "exa": {
      "apiKey": "replace-with-your-api-key",
      "timeoutMs": 55000
    }
  }
}

The JSON fields are provider, fallbackChain, and providers. Provider entries support apiKey, timeoutMs, provider-specific options, and credentialFile for Codex. Official endpoints for Codex, TinyFish, Tavily, Exa, and Firecrawl are built in and cannot be overridden. The default provider request timeout is 55 seconds. Restrict configuration and credential files to the owning user; do not commit secrets.

File paths and loading

  • Relative configFile paths resolve from the DSH process's current working directory, not the Cordis profile directory.
  • Both ~/ and ~\ expand to the user's home directory.
  • Relative providers.codex.credentialFile paths resolve from the JSON configuration file's directory, not the process working directory. This field also supports ~/ and ~\ home expansion.
  • The JSON configuration is loaded once at plugin startup. Restart the corresponding DSH process after changes; there is no live reload.
  • A missing default file logs a warning and uses empty configuration, with no credentials.
  • An explicitly specified missing file, invalid JSON, or a schema-invalid configuration throws a clear error rather than silently falling back.
  • The plugin no longer reads environment variables, including API keys, endpoints, timeouts, Codex credentials, and demo mode. Values in ~/.dsh/.env are not used by this plugin.

Override the JSON configuration path

To use a different file, add an id-targeted override to ~/.dsh/profiles/<profile>/cordis.patch.yml:

- id: search-providers
  config:
    configFile: ~/.dsh/search-providers-work.json

Omit configFile to use the default path. Do not put provider settings or secrets in this Cordis override.

Change the active provider or fallback chain

Set provider and fallbackChain in the JSON file, for example:

{
  "provider": "exa",
  "fallbackChain": ["codex", "tinyfish", "tavily", "firecrawl"],
  "providers": {
    "exa": {
      "apiKey": "replace-with-your-api-key",
      "timeoutMs": 55000
    }
  }
}

Routing rules:

  • provider is always attempted first when it is available and supports the requested capability.
  • fallbackChain contains the providers attempted afterward.
  • Duplicate provider ids are removed while preserving order.
  • fallbackChain: [] disables fallback and uses only the active provider.
  • Providers that do not support the requested capability are omitted from attempts and diagnostics.

For TinyFish, Tavily, Exa, and Firecrawl, provider entries support apiKey, timeoutMs, and provider-specific options. Generic options remains supported, but does not allow endpoint overrides. TinyFish uses built-in official endpoints for both search and page fetching.

Codex uses credentialFile and timeoutMs from its provider configuration. Its endpoint is fixed, and it intentionally ignores apiKey and options; credentials come exclusively from the file selected by providers.codex.credentialFile.

Migration from inline Cordis and environment settings

  1. Create the independent JSON configuration using the packaged example. Move inline Cordis provider, providers, and fallbackChain values into that JSON file. Old inline provider/fallback settings are rejected with a migration message; they are not merged or silently ignored.
  2. Move each provider's *_API_KEY and *_TIMEOUT_MS values into its JSON apiKey and timeoutMs fields. Move CODEX_CREDENTIAL_FILE to providers.codex.credentialFile and CODEX_TIMEOUT_MS to providers.codex.timeoutMs. Check relative credential paths against the JSON file directory. Do not migrate *_BASE_URL values: official endpoints are built in.
  3. Replace SEARCH_PROVIDERS_DEMO with "provider": "demo" in JSON when demo mode is wanted. Environment variables no longer configure any provider.
  4. Remove any existing provider baseUrl fields and TinyFish options.fetchBaseUrl from the JSON configuration; these legacy endpoint overrides are rejected. Remove an empty options object if nothing remains. Keep API keys, timeouts, routing (provider and fallbackChain), Codex credentialFile, and other supported provider options.
  5. Remove the old inline fields from the Cordis plugin configuration, leaving only an optional configFile. Restart DSH to load the new file.

Fallback, cooldown, and cancellation behavior

The plugin rechecks provider state before every attempt instead of relying on a stale snapshot. A provider whose cooldown expires while other providers are being tried may participate again in the same request.

Failure class Effect
Confirmed authentication failure Provider disabled globally until DSH restarts
HTTP 429/432, quota, or rate limit Current capability cooled for 5 minutes
Network error or timeout Current capability cooled for 30 seconds
Other request failure Current capability cooled for 10 seconds

Transient state is capability-scoped: a search failure does not cool web_fetch, and a fetch failure does not cool web_search. Authentication disablement is provider-wide.

The first eligible provider uses its configured request timeout. Each subsequent fallback attempt has an additional 55-second outer limit, which also stops providers that ignore AbortSignal. The bundle sets the native web_fetch tool's fetchTimeoutMs to 200 seconds to preserve the former page-reading aggregate deadline. These request deadlines are separate from the cooldown durations in the table above.

Caller cancellation is different from failure: it propagates immediately, preserves the original abort reason, does not continue fallback, and does not alter provider state.

Successful search output identifies the provider that actually answered and reports fallback, cooling, and disabled-provider state. Final errors list the providers genuinely attempted and the current live state.

Codex credential file

Codex uses the standalone ChatGPT Codex search endpoint and is independent of model/login plugins. It only reads an existing static credential file; it never starts a login, refreshes a token, checks local expiry timestamps, or writes credential data.

Minimal supported JSON:

{
  "access_token": "eyJ...",
  "account_id": "account-id"
}

The parser also accepts credential and tokens wrappers, access as an alternative token field, and accountId as an alternative account field. If the account id is omitted, the plugin reads the standard chatgpt_account_id claim from the access token. Missing or malformed credentials make Codex unavailable. Codex HTTP 401/403 responses are treated as confirmed credential failures and trigger the normal fallback chain.

Account-store files such as .openai-codex-auth.json are also supported: the parser reads access and accountId from the credentials array entry matching activeAccountId. Without an active selector, only a single-entry array is accepted; ambiguous or missing matches are not guessed. The store is read-only, and the original formats remain supported.

Restrict the credential file to the owning user. Codex does not implement web_fetch, and its standalone endpoint does not apply this plugin's recency, domain, location, language, or maximum-result filters.

Usage

After loading the plugin and configuring at least one provider, ask the model to search:

Search the web for "latest Rust memory safety features".

Successful results start with the provider that actually answered:

[Search provider: TinyFish]

Read a specific page with:

Read this page: https://example.com/article

web_fetch returns the native fetch result, with summary, extracted content, outgoing links, uncertainty, and operational warnings rendered in body.content.

Demo mode

Select the key-free demo provider in your JSON configuration file:

{
  "provider": "demo",
  "fallbackChain": []
}

Restart DSH after changing the file. Demo mode returns canned search and page-fetch results for integration testing; no environment variable is needed or read.

Development

bun install --frozen-lockfile
bun run typecheck
bun run test
bun run build

Main components:

  • src/providers/types.ts β€” provider interfaces and result types.
  • src/providers/registry.ts β€” provider registration and active-provider selection.
  • src/providers/cooldown.ts β€” authentication disablement and capability cooldown state.
  • src/providers/*.ts β€” provider implementations.
  • src/tools/web-search.ts β€” DSH web seam adapter and search fallback.
  • src/tools/read-page.ts β€” web_fetch registration and fetch fallback.
  • src/index.ts β€” plugin entry point and default provider order.

To add a provider, implement SearchProvider, register it in src/index.ts, and add its configuration resolution.

Publishing

  1. Bump the version in package.json.
  2. Run the development checks and inspect bun pm pack --dry-run.
  3. Log in with npm login.
  4. Publish the public scoped package with npm publish --access public.

Published npm versions are immutable; every release requires a new version number.

License

MIT. The Codex interoperability implementation was developed with reference to dsh-codex-connect (Apache-2.0); no runtime dependency on that package is required.