DeepSeek Harness:
Everything Is a Plugin
Install dsh in two minutes, learn to read its plugin tree, write your first plugin — and see why its architecture bets in the exact opposite direction from Pi.
On August 13, 2026, DeepSeek open-sourced something that is not a model and not a benchmark: the scaffolding that turns a model into an agent. It is called dsh, it is MIT licensed, and its entire thesis fits on the repo tagline — Everything is a Plugin.
That reads like marketing. It is not. In dsh, the model adapter is a plugin. The tool registry is a plugin. The session log is a plugin. The agent loop — the while-loop that decides when to call the model again and when to stop — is a plugin. The official coding agent is not the product; it is one default composition of these plugins, and you can take it apart.
This piece does two things. First, a working tutorial: get it running, understand what actually booted, write a plugin. Second, the part I find more interesting — a comparison with Pi, the other harness people are talking about, which arrives at the opposite answer to nearly every question dsh asks.
Part one — Tutorial
Run it in two minutes
The fastest path needs nothing but Node. This pulls the package and boots the web UI on http://127.0.0.1:3080:
npx @deepseek-ai/dsh webIf you want to read the source while you use it — which, for a harness whose whole point is that you will modify it, you probably do — build from the repo instead:
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh webPick a runtime mode
dsh ships several presets, and they are worth knowing before you start tuning anything, because each one is just a different plugin composition:
- Standard — the full toolset: file editing, shell, search. The default coding agent.
- Code — the model writes TypeScript to orchestrate multi-step work instead of emitting one tool call at a time.
- Minimal — bash and an editor, nothing else. Built for benchmarking, and the honest way to measure how much the harness is really contributing.
- Creator — for authoring your own presets, with runtime inspection of what you composed.
That Minimal mode is a quiet act of confidence. Most harnesses do not ship a switch that lets you subtract the harness.
Part two — The mental model
A plugin tree, not a program
Underneath dsh sits Cordis, a plugin framework the project vendors in. The one sentence worth memorising: plugins contribute services, typed events, and reversible effects to a shared context. Reversible is the load-bearing word — when a plugin unloads, everything it registered unwinds with it. That is what makes “swap the agent loop” a real operation rather than a slogan.
A running dsh is not a program with a main function. It is a tree composed at boot from ordered layers, each patching the one before it:
Two nouns organise this. A bundle distributes Cordis config rows plus the code they point at. A profile is a named composition stored in the Harness home — it lists a bundle stack and holds your patches. Both are declared in package.json under a dsh field: dsh.profile lists a profile’s bundle stack, dsh.bundle points at the bundle’s patch file.
The single most useful command while you are learning the system prints the composed result of all of that:
dsh --profile web --dump-configRun it before and after any change you make. It is the difference between guessing and knowing.
The services you will actually touch
Core packages register services on the shared context. These are the handles your plugin code reaches for:
| Package | Service | What it owns |
|---|---|---|
| core/session | ctx.sessions | Append-only event log for everything that happened |
| core/system-prompt | ctx.systemPrompt | Prompt assembly |
| core/tools | ctx.tools | Scoped tool registry |
| core/agent | ctx.agents | The Agent interface |
| core/agent-loop | ctx.agentLoop | The default driver — replaceable |
| llm/llm | ctx.llm | Model adapter seam |
Extension happens through three categories of event. Session events are durable and logged. Agent events (agent/*) give you a live registry and let you observe each step. Capability events let you supply policy and adapters without importing the loop at all — which is how you change behaviour without taking a dependency on the thing you are changing.
One turn, step by step
A turn spans zero or more steps; a step is one model request plus the tool calls it produces. Knowing where the hooks sit tells you where to intervene:
Terracotta entries are event names you can subscribe to. Grey entries are the work that happens between them.
Capability seams
The architecture’s central idea is the capability seam: a swappable interface with three roles — a Service Definition declaring the interface, a Service Provider implementing it, and a Consumer using it. Consumers never know which provider they got.
| Filesystem & subprocess | Shared execution environment; repoint it and Bash, PTY and LSP follow |
| LLM adapters | Which model provider answers a request |
| Shell backends | Local spawn or remote execution |
| Terminals | Persistent PTY sessions |
| Sandbox | Process confinement policy |
| Subagents | Anything from a child agent to a delegated turn |
Part three — Tutorial
Write your first plugin
A plugin is a TypeScript module exporting an apply function. The framework calls it with a context object, and you register capabilities on that context. The smallest possible version:
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin loaded!')
}If your plugin consumes a service, declare it in an inject array. The framework waits until those services exist before loading you — which is how a tree with no fixed boot order still resolves deterministically:
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(/* ... */)
}There are three equivalent shapes. Object form, when you want the metadata inline:
import type { Context } from '@deepseek-ai/cordis'
export default {
name: 'my-plugin',
inject: ['tools'],
apply(ctx: Context) {
// ...
},
}And class form, when your plugin is a service other plugins will inject:
import { Service, type Context } from '@deepseek-ai/cordis'
export default class MyService extends Service {
static inject = ['tools']
constructor(ctx: Context) {
super(ctx, 'myService')
}
}Clean up after yourself
This is the part that makes hot-swapping real. Anything with a lifetime goes inside ctx.effect(), and you return the teardown. When the plugin unloads, the returned function runs:
export function apply(ctx: Context) {
ctx.effect(() => {
const timer = setInterval(() => {
console.log('heartbeat')
}, 5000)
return () => clearInterval(timer)
})
}Mount it by adding a config row — there is no core to patch, so you extend dsh by placing your plugin beside the others rather than inside anything. If you publish it, tag the repo with the dsh-plugin topic so it shows up for everyone else.
Part four — Analysis
Two harnesses, two opposite bets
Now the interesting part. Put dsh next to Pi — Mario Zechner’s minimal terminal harness — and you get two architectures that disagree about almost everything, which makes each one legible in a way it is not on its own.
Subtraction versus structured addition
Pi’s philosophy is subtraction. It was built out of frustration with tools that kept accreting features, and the response was to cut the core down to four tools: read, write, edit, bash. The argument is that frontier models have been trained hard enough on agentic work that they already understand what a coding agent is — the model knows what bash is, it knows how files work. Bolting on a bespoke “search the codebase” tool spends system-prompt tokens without buying capability. So Pi keeps a thin core and pushes everything else — plan modes, subagent orchestration, MCP — out to TypeScript extensions you add back deliberately.
dsh does not do subtraction. It does addition, then shatters the result. Rather than distilling a thin core, it takes every layer of an agent system — model, tools, filesystem, shell, sandbox, session storage, subagents, and the agent loop itself — and makes each one replaceable. The official coding agent is one answer assembled from those pieces, and explicitly not the only one.
The granularity is a level deeper than Pi’s. Pi is fixed core, pluggable periphery. dsh has no irreplaceable core at all.
Who holds control?
Dig one level under the architecture and the two projects answer a different question: who should own control of the system?
Pi’s answer is the developer. Minimalism there is not aesthetic — it exists so you can see every step and change any of it. The trust is placed in the person writing the code, not in the agent at runtime. If a feature would make the core decide something on your behalf, Pi would rather not ship it.
dsh hands control progressively to the runtime. When even the scheduling logic of the agent loop can be replaced, the system is declaring that it has no opinion about how it should run. Every rule is redefinable while it is running.
These are two genuinely different attitudes toward complexity. Pi treats complexity as a burden to be cut away. dsh treats it as something to be structured and packed into plugins — because complexity you have deleted is complexity nothing new can grow out of.
The real gap: self-evolution
Here is where the distance actually opens up. dsh can already have an agent inspect the edge of its own capabilities at runtime, write a plugin on the spot, mount it, and call that freshly acquired capability in the same session. Give it a task, and it can add a plugin to solve that task and then shed it again. The reversible-effects design is precisely what makes shedding safe.
The caveats are real and worth stating plainly. This is experimental. Dynamic plugins live in memory, so a restart forgets them, and there is no automatic path from “the agent invented this” to “this is now a permanent plugin.” But the direction is open.
Pi’s extension mechanism is, by comparison, static: a human writes a TypeScript extension and installs it explicitly. The agent does not notice a capability gap mid-task, build a tool, and start using it. Which lands right back on the philosophical split — Pi leaves “who extends the system” to people; dsh is trying to give that job back to the agent.
Side by side
| Pi | DeepSeek Harness | |
|---|---|---|
| Core thesis | Cut until nothing is left to cut | Structure the complexity, then make every piece removable |
| Default tools | read, write, edit, bash | A full default set, itself a plugin |
| What is fixed | A thin core the model talks to | Nothing — including the agent loop |
| Extension unit | TypeScript extensions, skills, packages | Cordis plugins mounted as config rows |
| Who extends it | You, ahead of time, explicitly | You — or the agent, at runtime |
| Failure mode | You rebuild what you cut | You drown in composition surface |
| Maturity | Stable, proven, derivatives shipping | Developer preview, interfaces still moving |
Where the ceiling is
If I had to lean one way on long-run potential, I lean toward dsh — with the caveat that this is a bet on a direction, not a verdict on today’s software.
Pi has already proven its thesis. Minimal-and-extensible works; it has spawned derivatives that took off fast, and third-party evaluations have reported it sending dramatically less context per turn than heavier harnesses, which shows up directly as cost. But its ceiling is legible by design. The core philosophy is to manage as little as possible and leave extension to users and the community. That is a deliberate limit, not an oversight.
dsh is earlier, its interfaces are still moving, its plugin ecosystem is barely seeded, and its functional-programming idiom is a genuine on-ramp cost. What it has done is make “an agent can grow new capabilities itself” a first-class architectural property rather than an effect stitched together from outside tools. If that line pays off, the space it opens is an order of magnitude larger than a lean extensible harness — because the bet is not better building blocks. It is blocks that evolve on their own.
What to do with this
If you want to try the idea rather than read about it, the shortest useful path is: boot dsh web, run --dump-config to see what actually composed, then write the heartbeat plugin from Part three and watch it unwind cleanly when you unload it. That loop — compose, inspect, mount, unwind — is the whole system in miniature. Everything else is scale.
Questions people actually ask
- What is DeepSeek Harness (dsh)?
- DeepSeek Harness is an MIT-licensed, open-source agent harness that DeepSeek released on 13 August 2026. It is not a model and not a benchmark — it is the scaffolding that turns a model into an agent. Its thesis is that everything is a plugin: the model adapter, the tool registry, the session log, and even the agent loop itself are all replaceable plugins. The official coding agent is one default composition of those plugins rather than the product.
- How do I install and run DeepSeek Harness?
- The fastest path needs nothing but Node —
npx @deepseek-ai/dsh webpulls the package and boots the web UI onhttp://127.0.0.1:3080. If you want to read the source while you use it, clone the repository instead and runpnpm install,pnpm run build, thenpnpm dsh web. - What runtime modes does dsh ship with?
- Four presets, each just a different plugin composition. Standard is the full toolset — file editing, shell, search — and is the default coding agent. Code has the model write TypeScript to orchestrate multi-step work instead of emitting one tool call at a time. Minimal is bash plus an editor and nothing else, built for benchmarking how much the harness itself is contributing. Creator is for authoring your own presets with runtime inspection.
- What is Cordis, and how does it relate to dsh?
- Cordis is the plugin framework that dsh vendors in and builds on. Plugins contribute services, typed events, and reversible effects to a shared context. Reversible is the load-bearing word: when a plugin unloads, everything it registered unwinds with it, which is what makes swapping the agent loop a real operation rather than a slogan.
- How do I write a dsh plugin?
- A plugin is a TypeScript module that exports an
applyfunction; the framework calls it with a context object and you register capabilities on that context. Declare any services you consume in aninjectarray and the framework waits until they exist before loading you, which is how a tree with no fixed boot order still resolves deterministically. Anything with a lifetime goes insidectx.effect()and returns its own teardown. There are three equivalent shapes: a bare function, an object with inline metadata, and a class extending Service when your plugin is itself a service others will inject. - DeepSeek Harness vs Pi — which should I use?
- They answer the same question in opposite directions. Pi is subtraction: a fixed core of four tools — read, write, edit, bash — with everything else pushed out to extensions you add deliberately. dsh is structured addition: it refuses to designate a core at all, so every layer including the agent loop is the same kind of swappable thing. Practically, Pi is stable, proven, and will do your actual work today; dsh is an explicit developer preview whose APIs will break under you. Choose on that axis rather than on architectural elegance.
- Is DeepSeek Harness production-ready?
- No. It is an explicit developer preview and the maintainers say compatibility-breaking changes should be expected. Treat the exact APIs as a moving target and the architecture as the durable part.
- How do I inspect what dsh actually composed at boot?
- Run
dsh --profile web --dump-config. A running dsh is not a program with a main function but a tree assembled from ordered layers, each patching the one before it, so the composed config is the only reliable picture of what booted. Running it before and after a change is the difference between guessing and knowing.
Sources
Elsewhere on SaikoDev
- A harness is scaffolding around a model — the model itself is a separate choice. Notes on Claude and Cursor, which make the opposite trade: the architecture is decided for you.
- More long-form on how these tools are put together in the tech blog, or browse coding tools.