Yuki-takuya-kun
dsh-engine-switch
Route DeepSeek Harness top-level sessions to pluggable loop engines by agent-preset — Claude Code ships as a built-in engine.
- Stars
- 1
- Language
- JavaScript
- Created
- Aug 14, 2026
- Updated
- Aug 14, 2026
Introduction
dsh-engine-switch
English | 中文
A pure engine-switching runtime: routes DeepSeek Harness (DSH) top-level sessions to pluggable engines by agent-preset.
🚧 Pre-release — not yet usable. Routing/swap paths are not end-to-end validated. Do not use in production.
What it is
A standard + runtime that lets presets connect to engines while keeping engines and sessions transparent to each other:
- Engine = a driver implementing the DSH
Agentinterface (same interface as the native ReactLoop), registered by a downstream plugin. - This plugin ships no engine and no preset. It defines the integration contract and provides three things: preset → engine routing, swap while the session is blank, and preset installation.
Transparency:
- Engine is preset-unaware —
makeAgentreceivessession/options/engineConfig, never a preset. - Session is switch-unaware — the session drives a single
Agent; switching engines only swaps the agent driving it (while blank).
Install
dsh plugin --profile web add github:Yuki-takuya-kun/dsh-engine-switch
# then install at least one downstream engine plugin, e.g.:
dsh plugin --profile web add github:<your-account>/dsh-engine-claude-code
Enable
Edit ~/.dsh/profiles/web/cordis.patch.yml:
- id: dsh-engine-switch
config:
enabled: true
# optional: point a preset at an engine
# engineByPreset:
# research: claude-code
Restart the web app. Presets registered by downstream engines appear in the picker; selecting one routes to that engine, anything else → DeepSeek.
Registering a downstream engine
A downstream plugin injects engineSwitch:
export const inject = ["engineSwitch"];
export function apply(ctx) {
ctx.engineSwitch.register({
id: "claude-code", // engine id = preset directory name
name: "Claude Code",
description: "…",
presetDir: fileURLToPath(new URL("./presets/claude-code/", import.meta.url)),
makeAgent(loopCtx, id, options, session, engineConfig) {
return new ClaudeCodeAgent(loopCtx, id, options, session, engineConfig);
},
});
}
presetDir holds agent.cordis.yml + preset.yml; the framework installs it to ~/.dsh/.agent-presets/<id>/. See Engine API for the full contract.
How it works
- Replaces the agent factory and routes by preset:
engineByPreset[preset]→ "preset id = engine id" →defaultEngine. - Switching preset while blank (either direction) disposes the old agent and rebuilds a fresh session + the target engine's agent. DSH only allows switching before input, so the framework needs no extra lock.
- Subagents always delegate to DeepSeek; resume infers the engine from the session log's current preset (
resolveSessionPreset).
Config
| Key | Default | Meaning |
|---|---|---|
| enabled | false | master switch: false = plugin does nothing |
| defaultEngine | "deepseek" | engine for unlisted presets (and preset-less sessions); deepseek = delegate sentinel |
| engineByPreset | {} | preset id → engine id overrides |
| engines | {} | engine id → engine-private config (forwarded verbatim) |
Known limitations
- Pre-release: routing/swap not end-to-end validated.
- Swap is blank-only (DSH locks the preset once a session has input).
License
MIT. See THIRD_PARTY_NOTICES.md.