No description
  • TypeScript 83.3%
  • JavaScript 16.7%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-07 20:33:49 -04:00
examples init 2026-09-07 15:30:04 -04:00
scripts feat: settings section (GUI-editable), configurable route, default OPENCODE_API_KEY resolution 2026-09-07 18:10:48 -04:00
src fix(adapter): declare text-only input so image history projects to handle text 2026-09-07 20:33:49 -04:00
tests fix(adapter): declare text-only input so image history projects to handle text 2026-09-07 20:33:49 -04:00
.gitignore fix(apply): default config to {} — the loader passes undefined when no Config schema is exported 2026-09-07 17:09:13 -04:00
cordis.patch.yml add cordis patch 2026-09-07 15:42:21 -04:00
LICENSE init 2026-09-07 15:30:04 -04:00
package.json fix(adapter): declare text-only input so image history projects to handle text 2026-09-07 20:33:49 -04:00
README.md init 2026-09-07 15:30:04 -04:00

dsh-llm-opencode-go-session

A DeepSeek Harness (DSH) plugin: an OpenCode Go provider route whose every inference request carries x-opencode-session with the harness's stable per-conversation id.

OpenCode's managed-inference gateways (Go, Zen, Console Go — opencode.ai) started answering 400 MissingSessionID on 2026-09-05 for requests without that header. Upstream DSH's dsh-llm-pi-ai can only send static header values, which would collapse every conversation into one gateway affinity bucket — requests pass, but cache locality degrades and cost/latency go up. This plugin sends the real per-conversation id instead, which is what the gateway needs to route and keep each conversation's cache warm.

Fork-free: it registers the opencode-go route through DSH's public ctx.llm.registerAdapter() seam and implements the thin LlmAdapter on top of pi-ai's shipped opencode-go provider (endpoint, model catalog, and openai-completions / openai-responses / anthropic-messages protocols all come from @earendil-works/pi-ai/providers/opencode-go). Nothing in DSH is patched; delete the plugin the day upstream ships a sessionHeader profile field.

Status

  • DSH: tested against master c389f96 (2026-09-08), dsh-llm 0.1.3-alpha.2, @earendil-works/pi-ai 0.85.1.
  • Wire behavior verified by the test suite against a local mock gateway: every chat/completions request carries x-opencode-session: <conversation id> plus the gateway auth from OPENCODE_API_KEY, and SSE responses translate into harness chunks.
  • The Go gateway accepts the raw session-<uuid> conversation id (HTTP 200 at the wire, per the test suite and the discussion thread); no UUID re-formatting needed.

Install

Install the package into the same node_modules tree as DSH, then mount it in your profile's cordis.yml:

plugins:
  # config is entirely optional: see the full example below
  - id: llm-opencode-go-session
    name: dsh-llm-opencode-go-session

With no config, the adapter reads OPENCODE_API_KEY from the process environment (pi-ai's own provider auth). Everything else is optional:

- id: llm-opencode-go-session
  name: dsh-llm-opencode-go-session
  config:
    apiKeyEnv: OPENCODE_API_KEY   # resolve through the harness credentials seam instead of process.env
    sessionHeader: x-opencode-session

Pick the models on the opencode-go route (all 28 Go models, e.g. deepseek-v4-flash, glm-5.3-flash, plus the openai-responses Muse Spark and anthropic-messages routes) exactly like any pi-ai provider.

Important: keep opencode-go out of llm-pi-ai settings

Adapter routes are exclusive — one owner per route, all-or-nothing registration. If llm-pi-ai's settings also declare opencode-go, one of the two plugins fails at mount. Configure OpenCode Go only through this plugin; use llm-pi-ai for its other providers. If the route is already owned, this plugin skips with a loud warning instead of breaking the whole composition.

What is intentionally not covered

  • Images: the generic adapter refuses image blocks (UNSUPPORTED_CONTENT) — DSH's durable attachment service is not integrated. Text, tool calls, and tool results are fully supported.
  • Replay envelopes: finish chunks carry no adapter replay state, so assistant history is reconstructed from durable content (the same content-only posture DSH uses for foreign history). Fine for live use.
  • Settings UI / model discovery: no settings section is registered; the route appears in model pickers via listProviders()/listModels(). apiKeyEnv is the only config knob, set in cordis.yml.

Development

The package typechecks against a real DSH checkout (its built lib/types, so it compiles exactly as installed) and runs pure-Node tests against a local mock gateway:

DSH_REPO=/path/to/deepseek-harness node scripts/dev-setup.mjs   # once; creates node_modules links + tsconfig.check.json
node scripts/build.mjs            # strict typecheck (tsc) + transpile src/ -> lib/
node --test "lib/tests/*.spec.js" # 11 tests, incl. the wire-header assertion

node_modules/, lib/, and tsconfig.check.json are gitignored; the shipped surface is src/ + scripts/ + examples/.

License

MIT. Message/stream conversion adapted from @deepseek-ai/dsh-llm-pi-ai (DeepSeek Harness, MIT, © 2026 DeepSeek); see LICENSE.