
DeepSeek AI's coding-agent tools have mostly shipped as models. deepseek-harness (binary name dsh) is different: it's the harness — the agent loop, tool registry, sandboxing, session state, and UI that sit around a model — released as open source under the tagline "Everything is a Plugin."
It's currently a fast-moving developer preview, but the design is unusual enough to be worth understanding on its own terms, whether or not you plan to run it today.
This post covers both angles: how to get it running, and how it's built underneath.
Quick facts
Maker: DeepSeek AI
License: MIT
Status: developer preview — the README is explicit that breaking changes are expected
Core framework: Cordis, a TypeScript composition micro-kernel
Traction: ~210k GitHub stars and 24.5k forks as of this writing — unusually high for software still in developer preview
Getting started
Fastest path
If you have Node.js installed:
npx @deepseek-ai/dsh webThis starts the web UI at http://127.0.0.1:3080 and opens it in your default browser on a local machine. If you're running over SSH, it just prints the URL instead of trying to open a browser, since your terminal or editor owns the forwarded port. Add --no-open if you don't want it to try opening a browser at all.
Building from source
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh webpnpm run build compiles the repo's packages; pnpm dsh web then runs against those built artifacts without rebuilding each time.
A word on installing from anywhere else
Because the project is exploding in popularity, expect copy-cat packages and "wrapper" repos to show up under similar names. Stick to the deepseek-ai GitHub org and the @deepseek-ai/dsh npm scope, and treat any install method that asks you to pipe a downloaded script straight into a shell — or drop unknown code into an agent's skills/plugins directory — with real suspicion.
That's a generic supply-chain hygiene point, but it matters more than usual for a tool whose entire job is executing commands on your behalf.
Read the safety notice first
This is worth taking seriously rather than skimming. The project classifies itself as an unaudited, developer-preview tool — nothing about its security posture should be assumed. By design it can execute model-generated code and commands, load third-party plugins, and reach the network, filesystem, processes, and credentials available to it.
Sandbox and approval-prompt features exist, but the project is upfront that they reduce risk rather than guarantee isolation. The full checklist is at the end of this post; the short version is least privilege, a disposable environment, and a habit of reviewing what you're about to let it run.
See what actually boots
dsh --profile web --dump-configThis prints the full plugin tree your configuration resolves to. It's useful the moment you start wondering what's actually running under the hood — and given the architecture ahead, you will.
The core idea: no privileged core
Most agent tools — Claude Code, Cursor, Cline, OpenCode, and similar — ship as an opinionated, mostly monolithic product. The model, the tool surface, the memory handling, and the agent loop are bundled together; swapping one of them out usually means forking the repo. DeepSeek Harness takes the opposite bet.
Every functional piece of the product — the model adapter, the tool registry, the session log, even the agent loop itself — is a plugin running on top of Cordis, a general-purpose composition framework. There's no privileged core to patch. You extend the harness by mounting a plugin alongside the others, and each plugin's registrations are effects that cleanly unwind when it unloads.
That framing isn't just marketing copy. It's backed by an actual formal model, described in the paper behind Cordis, A Programming Paradigm for Spatiotemporal Composability (Shi, Zhang & Cui, 2026).
The paper's core move is to treat plugin composition as two separable problems: temporal composability — can a component's side effects be completely reverted when it's removed — and spatial composability — can components declare and reactively track dependencies on each other.
It answers both by giving every context change a paired inverse (a "revertible effect") and by classifying every context change against each component's declared dependencies (a "reactive coeffect"), unifying the two into what the paper calls the context paradigm. Cordis is the runtime implementation of that idea, including hot module replacement of the plugin tree.
The practical upshot for dsh: swapping a model endpoint, replacing the sandbox backend, or changing the entire UI is a configuration change, not a fork.
How a running instance gets composed
A running dsh process is a plugin tree assembled at boot from ordered layers, organized around two concepts:
A profile is a named composition stored in your Harness home directory. It lists which bundles to stack, any extra out-of-tree plugins to install, and your own local patch file.
webandheadlessship as built-in templates.A bundle is a distributable unit of Cordis configuration plus the code it mounts — packaged so that anything it inserts stays patchable by whatever layer sits above it.
dsh-base is the foundational bundle every profile starts from: model adapters, tools, persistence, sandbox and approval policy, settings, credentials, and telemetry. dsh-web-app layers the browser application on top; dsh-headless swaps in a one-shot runner with no server at all.
Layers apply in a fixed order: each bundle in the profile's listed sequence, then the profile's own patch file, then your home-level patch, then anything passed via --patch. A patch works by targeting a config row by ID, either replacing it outright or inserting new rows.
Core subsystems at a glance
Subsystem | Responsible for | Context key |
|---|---|---|
Session | The append-only event log and in-memory session store |
|
System prompt | Assembling prompt sections and tool schemas |
|
Tools | The scoped tool registry and its guarded execution pipeline |
|
Agent | The |
|
Agent loop | The default driver implementing the agent interface |
|
LLM | Message/stream types and the model-adapter seam |
|
Every one of these is itself a plugin mounted on the shared context. That's why the extension points below read like ordinary configuration rather than "here's where you'd need to patch the core."
Three domains of events
Cordis events are the extension surface, and picking the right kind is usually the first real design decision when adding something:
Session events are durable facts appended to the log and broadcast on
session/event. Use these when something needs to survive a reload.Agent events (
agent/*) carry a liveAgentobject — its inbox, step, status, and validation state. Use these to observe or intercept work while it's happening.Capability events (
fs/*,tools/*,telemetry/*, etc.) attach policy or adapters to a specific seam without needing to import the agent loop at all.
Anatomy of a turn
The vocabulary here is precise: a step is one model request plus whatever tool calls come out of it. A turn is zero or more steps — it opens before its first input is even claimed, and stays open until nothing more is owed. That ordering matters: a turn can open, fail to claim any usable input, and close having run zero steps, which is itself a fact worth logging.
In broad strokes, a turn works like this: the loop claims the next queued input, assembles the current prompt sections and tool schemas, and fires a pre-step hook that other plugins can use to rewrite or reject what the model is about to see.
Assuming it isn't rejected, the input gets appended to the log, the model request goes out over the streaming interface, the reply streams back and is logged, any resulting tool calls run through a pre-execute/execute/post-execute pipeline, and the step closes.
If a tool call owes another model turn, or new input has arrived in the meantime, the loop claims again and starts another step; otherwise a turn-stopping event fires and the turn closes.
Most of the interesting hooks in that sequence — the pre-step check, the model request, the streaming callback, and the three tool-pipeline stages — are "waterfall" events, meaning each listener must explicitly call through to the next one, so a plugin can veto or transform what happens next.
The turn-stopping event is different: it's a plain serial event, so every listener still runs, but none of them can veto it or hand off a rewritten value the way a waterfall listener can — by the time it fires, the turn is ending regardless.
The session log is the single source of truth
Everything upstream of the model is reconstructed from one append-only event log. A deriveMessages()-style projection builds the model-visible conversation from that stream, while the raw streamed chunks are kept separately for UI and replay fidelity. Forking a session, resuming one, generating transcripts, and telemetry all read from this same stream.
The architecture treats this as a hard invariant rather than a convention: if the model can see it, it must be reconstructable from the log. In practice that means adding any new kind of model-visible input requires extending the session event schema — you can't quietly thread new context into a request without also making it replayable.
Capability seams
A seam is how the harness makes a capability swappable. Each seam has three roles: a service definition (the interface), a service provider (an implementation), and a consumer (usually a model-facing tool that uses it).
A single package can play more than one role, but a seam needs all three roles filled by someone — a provider with no consumer, or vice versa, isn't a seam yet.
This is the mechanism behind claims like "swap one provider and the whole product moves." Filesystem access and subprocess execution share a single execution-world abstraction, so pointing that abstraction at a remote sandbox carries shell access, pseudo-terminals, and language-server integration along with it — no per-tool forking required.
Subagents work the same way: a "subagent provider" can be anything from a freshly spawned child agent to a delegated turn handed off to a different product entirely, behind one consistent interface.
Where new behavior actually goes
A few representative entries from the project's extension map:
You want to… | You hook into… |
|---|---|
Add a new model provider | Register an adapter on |
Add a model-facing tool | Register on |
Add or change shell execution | Register a |
Confine spawned processes | Provide a |
Intercept a request, tool call, or turn | Listen on the relevant |
Inject extra context into the next model request | Call |
Fork a live session |
|
The pattern across all of these is the same: you're never editing a central dispatcher, you're mounting a plugin that registers against a documented seam.
What this buys you — and what it costs
The genuine strengths:
Swappability is real, not aspirational: pointing the sandbox seam at a different backend moves several tools at once, because they share the same abstraction rather than each hard-coding their own execution path.
The durable session log gives you fork/resume/replay/telemetry essentially for free, because they're all views over one stream instead of separate features.
Headless vs. web, or a minimal profile vs. a fully loaded one, is a choice of which bundles to stack — not a maintained fork.
The real costs:
The architecture docs open with "read this before changing anything under
packages/, it assumes you know Cordis" — this is not a shallow learning curve, and understanding why something is a plugin is a prerequisite for touching almost anything.More indirection than a hard-coded agent loop means more places to look when something breaks, especially while the plugin/event vocabulary is still new to you.
It's a developer preview: breaking changes are expected by the project's own admission, and it hasn't been through a security audit.
The plugin ecosystem is brand new — there's an informal
dsh-plugintopic tag on GitHub for discoverability, but no mature registry or vetting process yet, which matters given that plugins run with real system access.
Where it fits
Most competing agent tools couple the model, the tool surface, and the loop into one product you'd have to fork to meaningfully change. DeepSeek Harness's bet is that those should be three separately swappable layers, held together by a plugin contract instead of shared source. That's a genuine architectural departure, not just a marketing line: the seam-based design in the sections above is what makes it true.
Whether it becomes the dominant pattern or stays a power-user option probably has less to do with the architecture itself (which is solid) and more to do with whether a plugin ecosystem with meaningful quality and trust signals forms around it. The star count says a lot of people are curious; it doesn't say how many are relying on it for real work yet.
Safety checklist before you point it at anything real
Worth repeating as a standalone list, since it's easy to skip past prose:
Run it with the least privilege and access it actually needs
Use a disposable VM or container rather than your main machine
Back up anything within its reach
Don't hand it credentials you're not prepared to lose
Review plugins, config, and proposed commands before letting them execute
Install only from the official
deepseek-aiorg /@deepseek-ai/dshnpm scope
Further reading
Repository: https://github.com/deepseek-ai/deepseek-harness
Architecture docs: https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/architecture.md
Safety notice: https://github.com/deepseek-ai/deepseek-harness/blob/master/SAFETY.md
Cordis paper: https://arxiv.org/abs/2608.25512
Full docs site: https://deepseek-harness.github.io/deepseek-harness/
Comments (0)
Login to post a comment.