DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-opencode-go-catalog

A

dsh-opencode-go-catalog

Manifest valid
hasBundlePatch

dsh-opencode-go-catalog

A self-maintained OpenCode Go model catalog for DeepSeek Harness (DSH). Versioned snapshot, refresh on command, on/off switch — full control, no upstream waiting.

Status: 0.1.0 · 19 models · snapshot 2026-10-11, incl. Step 5 Preview Free (step-5-preview-free)

Contents

Why

The built-in opencode-go catalog in @deepseek-ai/dsh-llm-pi-ai is a static build snapshot (~16 models at the time of writing) that never refreshes itself — new models like Step 5 are missing until upstream bumps the dependency. See Discussion #3957.

This plugin never touches the built-in route. It manages three sibling routes through the official settings.mutate seam:

RouteProtocolExamples
opencode-go-extrasopenai-completionsStep 5 Preview Free, GLM-5.3, LongCat-2.0, MiMo-V2.6
opencode-go-responsesopenai-responsesGPT-5.6/6 Luna, Grok 4.6/4.7, Muse Spark 1.2/1.3
opencode-go-messagesanthropic-messagesClaude Haiku 5.5, Qwen3.8 Max/Flash

The split is mandatory, not stylistic: some Go models are Responses-only (a chat-completions request answers with a bare 500 or 401 "not supported for format oa-compat"), others are Messages-only. One route = one protocol.

How it works

  1. catalog/models.json is the curated snapshot (protocol-separated, capacities verified).
  2. On startup, on an interval (default 4 h), and on /opencode-go-sync, the host module refreshes input / limits / reasoningEfforts from the live registry (https://models.opencode.ai/api.json, ETag cache + last-good fallback) and writes the three sibling routes via settings.mutate (change-only).
  3. Membership stays curated: newly appeared live IDs are reported, never auto-sorted into a route — no endpoint discloses the wire protocol, and a wrong guess fails at request time.
  4. The module never throws during boot (degrades to a warning + a ready-to-paste YAML block), never edits node_modules, and never reads or writes credentials (it only references the OPENCODE_GO_API_KEY name, same as the built-in route).

Installation

Prerequisites: DSH running, an OpenCode Go subscription with a working key (OPENCODE_GO_API_KEY already configured — if the built-in opencode-go route works, you are set).

Via Plugin Market (once listed): Settings → Plugin Market → search dsh-opencode-go-catalog → one-click install → restart → open the model picker. Expect three new groups (OpenCode Go extras / responses / messages) next to the built-in one.

Manual (until listed): copy this folder to <profile>/node_modules/dsh-opencode-go-catalog, add "dsh-opencode-go-catalog" to dsh.profile.bundles in the profile package.json (keep a backup first), restart DSH.

Without the plugin (static, no auto-refresh): generate the snippet and replace your llm-pi-ai row with it — see INSTALL.md and weg-a-schnellstart.yml:

node bin/snippet.mjs > snippet.yml

Configuration

Bundle row (cordis.patch.yml, replaces the whole row — no deep merge):

- id: opencode-go-catalog
  name: dsh-opencode-go-catalog
  config:
    enabled: true            # false → sibling routes are removed again
    refreshReasoning: true   # false → use snapshot file values only, no network
    sessionHeaderValue: 'dsh-opencode-go-catalog'  # false → no static session header (400 risk)
    intervalMinutes: 240     # 0 → startup only
    startupDelaySeconds: 10
    refreshTimeoutMs: 120000
    catalogPath: ./catalog/models.json
    managedRoutes:
      - opencode-go-extras
      - opencode-go-responses
      - opencode-go-messages

Commands & scripts

CommandEffect
/opencode-go-sync (chat)Immediate rewrite from snapshot + live registry. Registered through ctx.root into the global command layer, so it appears in the / menu for every session (registering through the plugin fiber context lands outside all agent scope chains: self-probe green, menu empty — fixed in 0.1.4)
node bin/sync.mjs --dry-run (default)Report: field diffs, new live IDs, stale candidates — writes nothing
node bin/sync.mjs --writeRewrite snapshot fields from the registry + stamp the date
node bin/sync.mjs --checkOffline snapshot rule check
node bin/snippet.mjsPrint the complete llm-pi-ai replacement row for manual installs
node verify.mjsManifest + patch shape + catalog rules (offline)

Fresh reasoning efforts

reasoning_options in the live registry are structured (effort values, toggle, budget_tokens) — Models.dev serves the same data. Mapping rules (lib/reasoning.mjs):

Registrypi-ai reasoningEfforts
effort values (low, medium, …)Identity mapping (low: low, …)
none valueoff: none
toggle-only / budget_tokens-only / empty arrayno block (never invent a menu — the picker would lie)
toggle + effort combinedeffort wins
modalities.input containing imageinput: [text, image] (pi-ai only knows text/image; video/audio/pdf are dropped documented)

Live examples: Step 5 Preview Free → {low, medium, high}; GPT-5.6 Luna → {off: none, low…max}; Muse Spark → {minimal…xhigh} (no max!); LongCat-2.0 / MiniMax M3 (toggle-only) → no menu.

Maintaining the catalog

  1. Run node bin/sync.mjs --dry-run.
  2. Take ~ field updates straight away with --write — they come 1:1 from the registry.
  3. Triage + IDs by hand (this includes built-in catalog models our snapshot deliberately doesn't duplicate — genuinely new are IDs in neither place). Probe both endpoints before sorting a newcomer into a route, then adopt with --write.
  4. node verify.mjs → /opencode-go-sync.

Deliberately excluded (unverified Oct 2026, see deliberatelyExcluded in the snapshot): glm-5, kimi-k2.5, mimo-v2-omni/pro, qwen3.5-plus, hy3-preview, space-bunny-free, minimax-m2.5.

Session header (MissingSessionID-400)

OpenCode Go requires x-opencode-session (stable per-conversation ID) since 09/05 — see Discussion #5495. pi-ai attaches it only inside its own opencode-go provider module; hand-declared routes are rebuilt "from parts" without that wrapper, so their requests fail with 400 MissingSessionID (telemetry: Spark path 74–90% coverage, everything else ~0%).

Until the adapter learns per-conversation sessionHeader, this plugin therefore sets a static header on every sibling route (sessionHeaderValue, default 'dsh-opencode-go-catalog', off with false). Trade-off, stated plainly: one fixed ID across all conversations means fewer cache hits — slower and pricier than a proper per-conversation ID (user-verified). It beats a hard 400. The built-in opencode-go route is never touched (its working Spark path stays as is). Remove the workaround the day the adapter ships sessionHeader support (upstream: earendil-works/pi#9326).

Enable / disable

  • Siblings off, plugin stays loaded: add your own - id: opencode-go-catalog row (with the FULL config — patches replace, never merge; copy cordis.patch.yml as the template) to your profile patch with enabled: false. The three routes are removed again via unset ops; the built-in opencode-go route keeps running untouched.
  • Whole plugin off: disabled: true on the bundle row (or the market's disable list), restart. Nothing needs deleting.

Diagnostics

After every sync attempt the plugin writes status.json next to package.json (time, ok, via, changed, command registration, fetch source, error). Open it in any editor — no log access needed. If the picker stays empty, send this file's content with your report.

Troubleshooting

SymptomCauseFix
Model answers 500 / 401 not supported for formatWrong protocol (e.g. Responses-only model on a completions route)Move it to the correct sibling route
model '…' needs an apiHand-declared route without apiSet api + baseURL + non-empty models (the plugin does this automatically)
Images rejected before sendinginput is [text]Set input: [text, image] per model (only with proven vision support)
No effort menureasoningEfforts missingAdopt from the registry's reasoning_options; mind off: none vs empty off
400 MissingSessionIDGateway needs x-opencode-session, adapter sends it only on catalog routesPlugin sets a static one per sibling route (see above); per-conversation fix needs an adapter update
Installed but seemingly idle—This plugin uses no shell hooks; check status.json next to package.json
Install gate refusesPeer range with stable lower boundNot applicable here (no @deepseek-ai/dsh* peers declared on purpose)

Never hand-edit node_modules/…/pi-ai/dist/providers/data/opencode-go.json — the next DSH update discards it silently. This snapshot is the place.

Security

  • No secrets: the plugin never reads, writes, logs, or ships credentials. OPENCODE_GO_API_KEY appears only as a name reference, exactly like the built-in route. Verified by pre-commit sweep.
  • Network: only two public, keyless JSON endpoints (models.opencode.ai/api.json, opencode.ai/zen/go/v1/models), with timeout + ETag cache + offline fallback to snapshot values.
  • Zero dependencies, no @deepseek-ai/dsh* peers (the install gate cannot refuse it), no shell hooks, fail-safe boot.

Compatibility

Built against DSH 0.2.0-rc.2 behavior (static pi-ai catalog, settings.mutate seam, optional command registry). Pure ESM, Node ≥ 18.

License & sources

MIT — see LICENSE.

Comments

Loading…