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
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:
web_search_callactions of typeopen_page— pages the server actually opened (highest trust; the#ws_call_id=…tracking fragment is stripped);url_citationannotations, when a gateway provides them (they override the hostname fallback title and can add snippets);- 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_KEYcredential (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 refThe 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:
| field | default | meaning |
|---|---|---|
apiKey | – | literal key; prefer the credential store |
apiKeyEnv | OPENCODE_GO_API_KEY | credential reference resolved per search |
baseURL | https://opencode.ai/zen/go/v1 | gateway endpoint; /responses appended |
model | deepseek-v4-flash | Responses 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-flashonly under chat/completions and list Grok/GPT/Muse under/v1/responses. Empirically the gateway also routes deepseek models on/v1/responsesand executes server-sideweb_searchthere (repeatedly verified with fresh, real results), matching DeepSeek's own Responses API, whereweb_searchis documented as server-side. If OpenCode ever restricts routing to the documented matrix, searches start failing withWEB_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_callaction — URLs from a model that never searched are memory, not sources.