Skip to content
dsh.fish
Bundle

dsh-web-search-opencode-go

Fix DSH web_search through OpenCode Go / Zen: auto-inject x-opencode-session. Drop-in patched fork of @deepseek-ai/dsh-web-search-deepseek (DeepSeek Harness).

Source
leeyoung1
License
MIT
Updated
Updated yesterday

Readme

# dsh-web-search-opencode-go

[中文说明](README.zh.md)

**Fix DSH `web_search` when the search provider is routed through OpenCode Go / OpenCode Zen.** The gateway rejects Anthropic Messages requests that do not carry `x-opencode-session`:

```text
Error from provider (Console Go): Request is missing x-opencode-session and cannot be routed efficiently.
Please see https://opencode.ai/docs/go/#where-can-i-use-it
```

This repository is a **drop-in patched fork of `@deepseek-ai/dsh-web-search-deepseek`** for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness). It automatically injects:

- `x-opencode-session` — the current DSH agent session id (or a configured id / provider-wide UUID fallback), and
- `x-opencode-client: dsh`

so the built-in `web_search` tool works again without touching the official DSH installation.

**TL;DR:** store an OpenCode Go key in DSH → set `web-search-deepseek.baseURL` → run the installer → restart `dsh web` → use `web_search` as usual. Details: [Usage](#usage).

## What this changes

This is **not** a new CLI and **not** a new `web_search` command. After
installation and a `dsh web` restart:

- your existing `web-search-deepseek` settings stay in place;
- every ordinary `web_search` call from the agent goes through the patched provider;
- the patched provider adds the OpenCode Go / Zen routing headers automatically.

No profile patch, no extra tool, and no new prompt are needed.

## The problem

DSH's built-in web search provider calls an Anthropic-compatible Messages endpoint:

```text
POST {baseURL}/messages
```

If you point it at OpenCode Go:

```yaml
web-search-deepseek:
  baseURL: https://opencode.ai/zen/go/v1
```

the upstream provider sends only its normal headers (`x-api-key`, `authorization`, `anthropic-version`, …). OpenCode Go additionally requires a session-routing header and returns `MissingSessionID` / HTTP 400 without it. The native `web_search_20250305` tool itself works on OpenCode Go; only the routing header was missing.

## The fix

This fork keeps the official provider behavior and adds:

- automatic `x-opencode-session` / `x-opencode-client` injection for `opencode.ai` endpoints;
- explicit `headers` and `sessionId` config fields for other gateways;
- a safe installer that copies the patched package into the DSH web profile at the path the bundle already resolves:

```text
~/.dsh/profiles/web/node_modules/@deepseek-ai/dsh-web-search-deepseek
```

The official package under your DSH installation (`/opt/homebrew/...`, `npm -g`, etc.) is **never modified**.

## Quick start

Requires Node.js 20+ (or just a working `dsh` + `pnpm`) and an existing DSH web
profile. There are two install paths; **Option A** is the one the plugin market
uses.

### Option A — install as a DSH bundle (recommended)

```bash
dsh plugin --profile web add github:leeyoung1/dsh-web-search-opencode-go
# restart dsh web
```

The package declares `dsh.bundle`, so `dsh plugin` automatically adds it to
`dsh.profile.bundles`. Its `cordis.patch.yml` disables the official
`web-search-deepseek` row and inserts this patched provider in its place.

After the npm package is published, the command gets shorter:

```bash
dsh plugin --profile web add dsh-web-search-opencode-go
# restart dsh web
```

### Option B — drop-in copy (offline / no pnpm)

This copies the package into the profile's `@deepseek-ai` slot, shadowing the
official package. It needs no package manager and works on macOS, Linux, and
Windows.

macOS / Linux:

```bash
git clone https://github.com/leeyoung1/dsh-web-search-opencode-go.git
cd dsh-web-search-opencode-go
./mount.sh
# restart dsh web
```

Windows (PowerShell or cmd):

```powershell
git clone https://github.com/leeyoung1/dsh-web-search-opencode-go.git
cd dsh-web-search-opencode-go
node .\bin\dsh-web-search-opencode-go.mjs
# restart dsh web
```

`mount.sh` is a POSIX convenience wrapper around the same Node installer.

### Non-default profile or DSH home

macOS / Linux:

```bash
DSH_PROFILE_DIR=/path/to/profile ./mount.sh
# or: DSH_HOME=/path/to/dsh-home ./mount.sh
```

Windows:

```powershell
$env:DSH_PROFILE_DIR = "C:\path\to\profile"
node .\bin\dsh-web-search-opencode-go.mjs
```

### Useful flags (Option B)

```text
--dry-run     show what would change, without touching the profile
--uninstall   move the patched copy aside and restore the previous entry
--help        usage and environment variables
```

Then restart `dsh web`. The running process must be restarted because DSH loads plugins at boot.

## Usage

### 1. Store the OpenCode Go API key in DSH

Open **Settings → Models** and store your OpenCode Go key under either:

- `DEEPSEEK_API_KEY` (the provider default), or
- `OPENCODE_GO_API_KEY` (then set `apiKeyEnv: OPENCODE_GO_API_KEY` in the config below).

The key is resolved on every search, so rotating it does not require a restart.

### 2. Point the built-in web search provider at OpenCode Go

In `~/.dsh/settings.yaml`:

```yaml
web-search-deepseek:
  baseURL: https://opencode.ai/zen/go/v1
  apiKeyEnv: DEEPSEEK_API_KEY   # default; OPENCODE_GO_API_KEY also works
  model: deepseek-v4-flash      # default
```

### 3. Install the patch and restart

Follow [Quick start](#quick-start) above, then restart `dsh web`.

### 4. Use it normally

There is no new command. Ask the agent in natural language:

```text
搜索一下 DeepSeek Harness GitHub 的最新信息
```

or have it call the tool directly:

```text
web_search: DeepSeek Harness GitHub
```

Expected: normal results; no `MissingSessionID` error. Each auxiliary search is
recorded in the DSH session log as `web/deepseek-search-llm-request`.

## Configuration

Your existing settings continue to work:

```yaml
web-search-deepseek:
  baseURL: https://opencode.ai/zen/go/v1
  apiKeyEnv: DEEPSEEK_API_KEY   # default; OPENCODE_GO_API_KEY also works
  model: deepseek-v4-flash      # default
```

Optional new fields:

```yaml
web-search-deepseek:
  baseURL: https://opencode.ai/zen/go/v1
  headers:
    x-opencode-client: dsh      # override any automatic header
  sessionId: my-fixed-session   # explicit OpenCode routing session
```

Header precedence:

1. explicit `headers['x-opencode-session']`
2. `sessionId` config
3. current DSH agent session id
4. provider-wide UUID fallback (programmatic calls outside an agent turn)

`x-opencode-client` defaults to `dsh` and can also be overridden.

## How installation works

There are two supported install paths:

- **DSH bundle (Option A):** `package.json` declares
  `dsh.bundle.patch = ./cordis.patch.yml`. `dsh plugin add` installs the package
  and adds it to `dsh.profile.bundles`; the patch disables the official
  `web-search-deepseek` row and inserts this provider under the same settings
  namespace (`web-search-deepseek`) and provider id (`deepseek-official`).
- **Drop-in copy (Option B):** the Node installer copies this package to
  `<profile>/node_modules/@deepseek-ai/dsh-web-search-deepseek`. Node resolves
  that path before the shared `profiles/node_modules`, so the existing official
  bundle row loads the patched provider instead.

Either way you keep your existing `web-search-deepseek` settings; no settings
rewrite is required.

For the drop-in copy, if the destination already exists it is moved aside to
`dsh-web-search-deepseek.unpatched-<timestamp>` instead of being deleted. After
a DSH upgrade or a `pnpm`/`npm` reinstall that restores the official package,
run the installer again and restart `dsh web`.

## Uninstall / rollback

Use the cross-platform uninstaller on any OS:

```bash
npx dsh-web-search-opencode-go --uninstall
# or, from a clone:
node ./bin/dsh-web-search-opencode-go.mjs --uninstall
# restart dsh web
```

It moves the patched copy to `dsh-web-search-deepseek.uninstalled-<timestamp>`
and restores the most recent `.unpatched-*` entry when one exists. If no backup
exists, Node resolves the official package again.

Manual equivalent:

- macOS / Linux:

  ```bash
  rm -rf ~/.dsh/profiles/web/node_modules/@deepseek-ai/dsh-web-search-deepseek
  # restart dsh web
  ```

- Windows PowerShell:

  ```powershell
  Remove-Item -Recurse -Force "$env:USERPROFILE\.dsh\profiles\web\node_modules\@deepseek-ai\dsh-web-search-deepseek"
  # restart dsh web
  ```

## Compatibility

- DSH `0.1.x` line (verified on `0.1.1-rc.2`).
- The patched provider is API-compatible with `@deepseek-ai/dsh-web-search-deepseek@0.1.1-rc.2`.
- OpenCode Go / OpenCode Zen Anthropic-compatible endpoint.
- Node.js 20+.
- macOS, Linux, and Windows. The installer copies files and uses no symlinks
  or administrator-only APIs; CI runs the installer test on all three OSes.

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| Still `MissingSessionID` after install | `dsh web` was not restarted | Restart `dsh web`. |
| `WEB_PROVIDER_CREDENTIAL_MISSING` | No key stored for `DEEPSEEK_API_KEY` | Store it in DSH Settings → Models, or set `apiKeyEnv: OPENCODE_GO_API_KEY`. |
| `DeepSeek search request failed` / HTTP 401/403 | Wrong key for the selected endpoint | Use an OpenCode Go key for `opencode.ai`, or point `baseURL` back at DeepSeek's official endpoint with a DeepSeek key. |
| Provider unavailable | Unparseable `baseURL` | Use the full base that includes `/v1`, e.g. `https://opencode.ai/zen/go/v1`. |
| Search broke after a DSH upgrade | The profile restored the official package | Re-run the installer (`./mount.sh`, `npx dsh-web-search-opencode-go`) and restart. |
| `EPERM` / `EBUSY` while installing on Windows | `dsh web` is running and the profile files are locked | Stop `dsh web`, run the installer again, then restart. |

## Development

```bash
npm test                 # header + cross-platform installer tests (dependency-free)
npm run link-deps        # optional: link official packages for full-provider imports
node ./bin/dsh-web-search-opencode-go.mjs --dry-run
```

Tests only import `lib/headers.js`, which has no dependencies. `link-deps` is
only needed when importing the full `lib/index.js` provider in a clone; the
installed copy resolves the official packages through the profile's
`node_modules` parents.

## Repository metadata (for search)

Suggested GitHub repository name, description, and topics:

- **Name:** `dsh-web-search-opencode-go`
- **Description:** `Fix DSH web_search through OpenCode Go / Zen: auto-inject x-opencode-session. Drop-in patched fork of @deepseek-ai/dsh-web-search-deepseek.`
- **Topics:** `dsh`, `deepseek-harness`, `deepseek`, `opencode`, `opencode-go`, `opencode-zen`, `web-search`, `websearch`, `dsh-plugin`, `x-opencode-session`, `MissingSessionID`, `cross-platform`, `windows`, `macos`, `linux`

See [docs/publishing.md](docs/publishing.md) for ready-to-run `gh` and `npm` commands.

## License and attribution

MIT. The provider implementation is derived from
[`@deepseek-ai/dsh-web-search-deepseek`](https://www.npmjs.com/package/@deepseek-ai/dsh-web-search-deepseek)
by DeepSeek, also MIT-licensed. The original license is preserved in [LICENSE](LICENSE).

This is an unofficial community patch and is not affiliated with DeepSeek or OpenCode.

Install

dsh plugin --profile web add github:leeyoung1/dsh-web-search-opencode-go

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source