Architecture
How DeepSeek Harness is put together — Cordis, profiles, bundles, events, the turn flow, and capability seams. This is a guided tour of the official architecture.md; read the original before changing anything under packages/.
The big picture
A running dsh is a plugin tree composed at boot from ordered layers. There is no privileged core: the model adapter, tool registry, session log, and agent loop itself are all plugins that register services, typed events, and reversible effects on a shared Cordis context — so every part is replaceable from configuration.
Cordis
Cordis is the framework under dsh. Plugins contribute to a shared context, and registrations are effects that unwind when a plugin unloads — this is what makes “everything is a plugin” more than a slogan. You extend dsh by mounting a plugin beside the others, never by patching a core.
Profiles and bundles
- Profile — a named composition stored in the Harness home. It lists the bundles it stacks, holds any out-of-tree plugins it installs, and keeps the user's own
cordis.patch.yml.webandheadlessship as templates. - Bundle — a distribution format for Cordis config rows and the code they mount. Whatever a bundle inserts stays patchable by the layers above it.
Each declares itself in its own package.json under a dsh field: dsh.profile lists a profile's bundles, and dsh.bundle points at a bundle's patch file.
dsh-base is the first layer of every profile: model adapters, tools, persistence, sandbox and approval policy, settings, credentials, telemetry. dsh-web-app adds the browser application; dsh-headless adds a one-shot runner with no server at all.
Layers apply to an empty entry list in this order: each bundle in the profile's listed order → the profile's cordis.patch.yml → the home-level patch → any --patch overlay. A patch targets a row by id and replaces its whole config, or inserts new rows. See Plugin Development for the full packaging walkthrough.
Core packages
| Package | Owns | ctx key |
|---|---|---|
core/session | The append-only SessionEvent log and in-memory store | ctx.sessions |
core/system-prompt | Prompt-section and tool-schema assembly | ctx.systemPrompt |
core/tools | The scoped tool registry and guarded execution pipeline | ctx.tools |
core/agent | The Agent interface, live registry, and agent/* events | ctx.agents |
core/agent-loop | The default driver implementing that interface | ctx.agentLoop |
core/scope | The per-agent scoped-registration primitive | library, no key |
llm/llm | Message and stream vocabulary plus the adapter seam | ctx.llm |
Events are the extension points
Events are how plugins hook in — and picking the right domain is the first decision in most changes:
- Session events — durable facts appended to the log and broadcast through
session/event. Use one when the fact must survive a reload. - Agent events (
agent/*) — carry a liveAgent: inbox, step, status, request, validation, continuation. Use them to observe or intercept work in flight. - Capability events — attach policy and adapters to a seam (
fs/*,tools/*,telemetry/*) without importing the loop.
The turn flow
A step is one model request plus the tools it calls. A turn is zero or more steps: it opens before its first input is claimed and closes once nothing is owed.
claim next-step input + one queued message
assemble prompt sections + tool schemas
step/start
agent/request -> llm/stream -> assistant/chunk* -> assistant/message
tool/call* -> tools/execute -> tool/result*
step/end
turn/end
turn/*, step/*, user/message, assistant/*, and tool/* are durable session events; the rest are live extension points. Waterfalls (agent/pre-step, agent/request, llm/stream, the tools/* events) must call next() to delegate; agent/turn-stopping is serial and has no next().
Session log & the model-visible rule
The session log is the source of the context the model sees. deriveMessages() projects model history from it, and raw assistant/chunk events preserve replay and UI fidelity. Fork, resume, transcripts, telemetry, and persistence all derive from this stream. The invariant: model-visible means logged — anything that reaches a model request must be reconstructable from the log.
Capability seams
A seam is a swappable capability with three roles: a Service Definition declaring the interface, a Service Provider implementing it, and a Consumer using it — commonly a model-facing tool. One provider swap changes the whole product: filesystem and subprocess providers share one execution world, so pointing them at a remote sandbox moves Bash, PTY, and LSP with them.
Where new behavior goes
| Goal | Mechanism |
|---|---|
| Add a model provider | register its adapter on ctx.llm |
| Add a model-facing capability | register on ctx.tools; its schema joins prompt assembly |
| Add shell execution | register a ctx.shell backend |
| Add a human command | register on ctx.commands; dispatches without a model turn |
| Add background work | register on ctx.jobs; job_* tools collect or stop it |
| Add filesystem access or policy | register a ctx.fs provider or listen to fs/* events |
See your own tree
# every row it prints can be replaced by a patch of your own
$
This is the fastest way to understand the composition and prototype a plugin.
Questions or feedback? Official Discussions is the project’s canonical support channel; Discord has an active community. dsh.so itself improves via plugin submissions and your feedback.