Deep dive14 min read

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:

bash
npx @deepseek-ai/dsh web

If 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:

bash
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web

Pick 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:

APPLIED IN ORDERdsh-basemodel adapters · tools · persistence · sandbox · settings · credentials · telemetrydsh-web-app / dsh-headlessbrowser UI, or the one-shot runner with no serverprofile patchthe named composition you bootedhome-level patchyour machine-wide overrides in the Harness homeCLI overlaysflags for this run only
Boot composition: dsh-base lays the foundation, a frontend layer goes on top, then progressively more local patches. Later layers target rows by ID and either replace a config entirely or insert new entries.

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:

bash
dsh --profile web --dump-config

Run 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:

PackageServiceWhat it owns
core/sessionctx.sessionsAppend-only event log for everything that happened
core/system-promptctx.systemPromptPrompt assembly
core/toolsctx.toolsScoped tool registry
core/agentctx.agentsThe Agent interface
core/agent-loopctx.agentLoopThe default driver — replaceable
llm/llmctx.llmModel 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:

turn/startclaim inputassemble promptsagent/pre-stepstep/startmodel requesttool executionstep/endagent/turn-stoppingturn/end

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 & subprocessShared execution environment; repoint it and Bash, PTY and LSP follow
LLM adaptersWhich model provider answers a request
Shell backendsLocal spawn or remote execution
TerminalsPersistent PTY sessions
SandboxProcess confinement policy
SubagentsAnything 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:

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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.

PI — FIXED CORE, OPEN PERIMETERDSH — NO PRIVILEGED COREread · writeedit · bashextensionskillpackagemodeltoolsfilesystemshellsandboxsessionssubagentagent loopsame shape, same weight — even the loop
Pi protects a small core and opens the perimeter. dsh refuses to designate a core — every slot in the tree is the same kind of thing, and every one of them is swappable.

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

PiDeepSeek Harness
Core thesisCut until nothing is left to cutStructure the complexity, then make every piece removable
Default toolsread, write, edit, bashA full default set, itself a plugin
What is fixedA thin core the model talks toNothing — including the agent loop
Extension unitTypeScript extensions, skills, packagesCordis plugins mounted as config rows
Who extends itYou, ahead of time, explicitlyYou — or the agent, at runtime
Failure modeYou rebuild what you cutYou drown in composition surface
MaturityStable, proven, derivatives shippingDeveloper 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 web pulls the package and boots the web UI on http://127.0.0.1:3080. If you want to read the source while you use it, clone the repository instead and run pnpm install, pnpm run build, then pnpm 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 apply function; the framework calls it with a context object and you register capabilities on that context. Declare any services you consume in an inject array 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 inside ctx.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.

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.