Back to home@bailynlove

web-search-opencode-responses

dsh WebSearchProvider over the OpenCode Zen Go Responses API server-side web_search tool

Stars
1
Language
JavaScript
Created
Aug 21, 2026
Updated
Aug 21, 2026
GitHub repo

Introduction

web-search-opencode-responses

A DeepSeek Harness (dsh) WebSearchProvider plugin that runs web search server-side through the OpenAI Responses API on the OpenCode Zen Go gateway — so web_search bills to your OpenCode plan instead of consuming DeepSeek API quota.

The official web-search-deepseek provider is Anthropic-bound: it speaks DeepSeek's Anthropic-compatible Messages API (POST /messages on https://api.deepseek.com/anthropic/v1), a wire that only ever reaches DeepSeek's own endpoint — it cannot be pointed at OpenCode. This plugin exists to bypass that limitation: it swaps in the OpenAI Responses API (POST /responses with the native web_search server tool) against https://opencode.ai/zen/go/v1, so the same WebSearchProvider seam runs search on the OpenCode plan. The only thing carried over from the official provider is the working principle — one search is one model call whose inference backend executes the search; the wire, endpoint, and billing are all different.

How sources are derived

The gateway does not populate url_citation annotations, so the provider maps:

  1. web_search_call actions of type open_page — pages the server actually opened (highest trust; the #ws_call_id=… tracking fragment is stripped);
  2. url_citation annotations, when a gateway provides them (they override the hostname fallback title and can add snippets);
  3. as a fallback, absolute URLs in the final answer text — what the model reports having found after searching.

If none of these yield a source, the search fails loud (WEB_PROVIDER_ERROR) instead of returning an unsourced answer.

Install (into a web profile)

The package ships as a dsh bundle: one install command both adds the plugin dependency and applies the patch layer that points the web seam at this provider.

From a profile directory (or with --profile <name>), install from GitHub:

dsh plugin --profile web add github:bailynlove/web-search-opencode-responses

That links the package, appends it to dsh.profile.bundles, and applies its cordis.patch.yml — inserting the web-search-opencode-responses row and setting web.searchProvider: opencode-responses. Verify the layer, then boot:

dsh --profile web --dump-config   # shows a "# == web-search-opencode-responses" layer
dsh --profile web

To pin a specific release instead of the moving default branch, use a commit:

dsh plugin --profile web add github:bailynlove/web-search-opencode-responses#<sha>

For a local checkout during development, keep the older file: wiring: declare "web-search-opencode-responses": "file:./plugins/web-search-opencode-responses" in the profile package.json and add the same two patch rows (insert the plugin, set web.searchProvider: opencode-responses) to the profile's cordis.patch.yml.

Configuration

The plugin registers a settings section (web-search-opencode-responses) on the Host. Note: the dsh web Plugin configuration tab only shows cards for the provider namespaces the web client ships (DeepSeek, Shell, Agent loop) — a third-party namespace like this one has no client card yet, so configure it through the settings document instead:

  • key — visual: dsh web Settings → Models, write the OPENCODE_GO_API_KEY credential (stored in ~/.dsh/.credentials.yaml).

  • endpoint / model / key reference — edit the settings document ~/.dsh/settings.yaml, opened in the web UI by the Open configuration file button:

    web-search-opencode-responses:
      baseURL: https://opencode.ai/zen/go/v1   # optional; /responses is appended
      model: deepseek-v4-flash                  # optional; server-side search model
      apiKeyEnv: OPENCODE_GO_API_KEY            # optional; credential reference
      apiKey: ""                                # optional; literal key over the ref
    

    The file hot-reloads, so a change applies on the next search; the provider reads the section per search, no restart needed.

Equivalent static values can be authored in the plugin's cordis.patch.yml entry (the composition base); the settings document overrides it. See the Configuration field table below:

fielddefaultmeaning
apiKeyliteral key; prefer the credential store
apiKeyEnvOPENCODE_GO_API_KEYcredential reference resolved per search
baseURLhttps://opencode.ai/zen/go/v1gateway endpoint; /responses appended
modeldeepseek-v4-flashResponses model with server-side search

The key resolves through dsh's credentials service first (the web Models page writes ~/.dsh/.credentials.yaml), then the launching environment; a stored secret never leaves the machine — the wire surface redacts it.

Tests

node --test test/mapping.test.mjs test/provider.test.mjs   # unit, offline
node test/integration.mjs                                  # real API, needs the key

Notes

  • Undocumented routing. OpenCode's go docs list deepseek-v4-flash only under chat/completions and list Grok/GPT/Muse under /v1/responses. Empirically the gateway also routes deepseek models on /v1/responses and executes server-side web_search there (repeatedly verified with fresh, real results), matching DeepSeek's own Responses API, where web_search is documented as server-side. If OpenCode ever restricts routing to the documented matrix, searches start failing with WEB_PROVIDER_ERROR — loud, never fake. Note the officially listed responses models are not a fallback: grok-4.5 accepted the tool but executed no search and answered from memory.
  • Each search consumes one model round; the server injects search results into the model context (≈25k input tokens observed) — billed to OpenCode.
  • Search quality is the model's: it rewrites queries and opens pages autonomously across several rounds.
  • The answer-text URL fallback engages only when the output contains at least one executed web_search_call action — URLs from a model that never searched are memory, not sources.