The short version: DeepSeek Harness's (DSH) "everything is a plugin" is not a slogan — it is carried by a plugin runtime with
years of history, a paper behind it, and formal proofs. That runtime is called Cordis. DSH vendors it (a source-level copy) into its
monorepo, renames it to @deepseek-ai/cordis, version 4.0.0-rc.7. It exists to solve the most counterintuitive problem in
plugin systems: loading is easy, unloading is hell. Most frameworks can attach a plugin's functionality; very few can remove it
cleanly without restarting the host. Cordis answers with two concepts — and those two concepts are exactly the "revertible effects" and "reactive
coeffects" in the title. All facts are as of August 14, 2026, drawn from the DSH repo (deepseek-ai/deepseek-harness):
docs/cordis-primer.md, docs/architecture.md, the four preset config files, and the Cordis paper.
A word on sourcing discipline first, because it decides what counts as "written in the source" versus "inferred."
Confirmed (verbatim from the repo): Cordis's five core concepts, the lifecycle semantics of effect/disposer/fiber, DSH's
profile/bundle/patch layering, the core packages' ctx keys, and the line-by-line contents of the four presets'
preset.yml and agent.cordis.yml. From the paper, stated with attribution: the precise definitions of
"revertible effects" and "reactive coeffects" (they come from the preprint A Programming Paradigm for Spatiotemporal Composability, a
collaboration between Peking University and the DeepSeek-AI Harness team, draft dated August 13, 2026, ~88 pages). Flagged as uncertain
where noted. As always on this site: we'd rather write "unverified" than invent a plausible-looking detail.
Table of contents
- 1. Why "everything is a plugin" deserves one more layer
- 2. What Cordis is: lineage, the paper, and being vendored into DSH
- 3. Cordis's five core concepts
- 4. The two key terms: Revertible Effects and Reactive Coeffects
- 5. The mechanics of effects: disposer, fiber, reverse-order teardown
- 6. How DSH uses Cordis: the plugin tree, seams, and "no privileged kernel"
- 7. The four run modes in detail: Standard / PTC(Code) / Minimal / Creation
- 8. Conclusion: what it all actually means
1. Why "everything is a plugin" deserves one more layer
Our previous post defined "everything is a plugin" as: in DSH, the model adapter, tool registry, session log, sandbox, storage, scheduler, UI, and even the agent loop itself are all plugins mounted on Cordis — and the way to extend DSH is not to touch source code but to "mount new plugins alongside others." That is correct, but it answers only "what," not "why." The natural follow-up is: why can plugins be swapped, hot unplugged, and auto-ordered by dependency? If it were just "split the code into modules and require them one by one," it would be no different from plugin systems a decade old, and there would be no reason for DeepSeek to call out Cordis by name.
The answer is that Cordis offers not "modularity" but composability — along two orthogonal dimensions: temporal (when a component is removed, every trace it left can be precisely undone) and spatial (a component missing its dependencies sits quietly until they are ready). Nail both dimensions and you can afford to make the model itself a swappable plugin — swap the model adapter under the same sessions, tools, and permission system, without a restart or rebuilding context. That is the deepest difference from Claude Code / Codex and their vertically integrated model-plus-product approach.
2. What Cordis is: lineage, the paper, and being vendored into DSH
First, a correction to a likely misreading: Cordis is not a wheel DeepSeek spun up for DSH. Its author is shigma, creator of Koishi, the open-source cross-platform chatbot framework (named after a Touhou Project character, ~6,000 GitHub stars). Cordis has run in production for about four years as Koishi's foundation, battle-tested by 4,000+ community plugins. Koishi ran Cordis v3; DSH's developer preview ships Cordis v4 — on the same day the paper was published. In other words, DeepSeek chose a framework that has been beaten on by real users for four years, not a lab prototype — a point both the docs and the paper emphasize as evidence that Cordis is proven, not experimental.
The paper is A Programming Paradigm for Spatiotemporal Composability. Its core question: modern plugin systems mostly cannot truly unplug a plugin. It names VSCode specifically — 87 of the top 100 marketplace extensions contain executable code that cannot be uninstalled at runtime without restarting the extension host. For a self-evolving agent harness, the pain is amplified: an agent may generate, install, and replace its own tools at runtime, and a restart destroys accumulated context and cache. Cordis is aimed squarely at that.
Then look at how DSH "acquired" Cordis — this says a lot about DeepSeek's attitude. Rather than depending on Cordis via npm, DSH
source-vendors Cordis and its foundation libraries (cosmokit, schemastery, loader, include, group, timer, hmr, and more) into its
monorepo, renaming them into the @deepseek-ai scope (cordis → @deepseek-ai/cordis,
@cordisjs/plugin-* → @deepseek-ai/cordis-plugin-*). The official rationale is blunt: "so that the harness fully owns
its framework layer (auditable, patchable, pinned)." The vendor directory also keeps a "local modifications log" recording every divergence from
upstream — for example, three reentrant-disposal hardening fixes to the fiber lifecycle, and transactional hot-reload for the Loader/Include config.
This "grip the framework layer too" posture matches DeepSeek's habit of "run it internally first, then ship."
3. Cordis's five core concepts
The official primer (docs/cordis-primer.md) condenses Cordis into five ideas. They are the foundation for everything that follows and are
worth unpacking one by one, because they correspond to five verbs: mount, find, wait, signal, and undo.
- A plugin is an object that implements Service. It can be a function with optional
injectandapply(ctx)fields, or aServicesubclass whose lifecycle Cordis mounts into the current context. Note the "object/function" shape — there is no required base class, so a plugin can be a row in YAML config or an inline function in code. - A context is a repository of services. A service claims a stable
ctx.<key>slot such asctx.tools,ctx.llm, orctx.sessions; other plugins find services by key instead of importing a concrete implementation. This is the root of "swappable models" — a consumer only knows there is actx.llm, never whether it is DeepSeek or OpenAI behind it. - Declare service dependency via
inject. A plugin that names required services waits until they exist, so load order is expressed through service requirements rather than manual boot sequencing. This flows directly into the "reactive coeffects" of the next section. - Typed events for communication. Services declare event names through TypeScript declaration merging, then dispatch them as
emit(observe),waterfall(wrap),parallel(fan out), orserial(run in order). Events are the extension points — the technical source of DSH's "events are extension points." - Registrations are reversible effects. Prompt sections, tool schemas, adapters, providers, and listeners are installed through
ctx.effect()orctx.on()so reload and teardown unwind them predictably. This one is the soul of the framework — the next two sections are devoted to it.
4. The two key terms: Revertible Effects and Reactive Coeffects
A terminology correction is due here, because these two words get mangled in translation and yet they are the paper's central contribution. The two concepts in the title map to the paper's English originals as follows:
| Chinese | English original | Problem solved |
|---|---|---|
| 可逆效应 | Revertible Effects | Temporal composability |
| 反应式余效应 | Reactive Coeffects | Spatial composability |
Start with Revertible Effects. The paper defines it strictly: every transformation of a context must carry an explicit inverse function tracked by the runtime; unloading a plugin executes this "undo chain" in reverse registration order, restoring the system precisely to its pre-load state. In everyday code terms: every registration returns a disposer, and the framework runs it for you on unload. "Revertible" is not rhetoric — it is a promise backed by operational semantics, and the paper proves a confluence property: the final state does not depend on the order of loads and unloads.
Now the more interesting one: Reactive Coeffects. This is where translations go wrong most often: it is not "reactive effects," but
coeffect — the dual of the "effect" in type theory's effect systems. An effect system asks "what side effects does this code produce?";
a coeffect system asks the reverse: "what does this code require from its environment?" Mapped onto Cordis: a component declares the
dependencies it needs (say, a chat plugin needs a message adapter and a database); until all are satisfied, it stays INACTIVE; once
they are ready, its lifecycle (activate / deactivate / neutral) is driven reactively by environmental change. That is where the
"reactive" comes from — and it is the theoretical basis for the inject declarations in the previous section.
The paper's elegance is that it unifies these two — normally from two separate bodies of theory — into a single context type, forming what it calls the "context paradigm." It gives a 10-rule operational semantics and proves four metatheoretic properties: preservation, global spatiotemporal composability, progress, and confluence. This post relays those terms without re-deriving them — they belong to the formal half of the 88-page preprint. The takeaway for a general reader is one sentence: Cordis is a mechanism where "unloading is fully undoable" and "activate only when dependencies are ready" are both true at once.
5. The mechanics of effects: disposer, fiber, reverse-order teardown
What does the theory look like in code? Chapter 2 of the Cordis tutorial (docs/cordis-tutorial/02-lifecycle-and-effects.md) gives the
bluntest answer: a plugin can be unloaded by a config edit, hot reload, explicit disposal, or loss of a required service. Registrations made through
Cordis APIs are effects and are undone when their owning plugin unloads; resources managed outside those APIs (a timer, a connection,
a file watcher) must be wrapped in ctx.effect() and return a disposer. The canonical snippet:
ctx.effect(() => {
const timer = setInterval(() => console.log('tick'), 200)
return () => { // ← this is the disposer
clearInterval(timer)
console.log('cleaned up')
}
})
This reveals the two key moves of the effect model: the effect body runs during load; the disposer it returns runs during unload; and
for a plugin-lifetime resource, you never call the disposer yourself — the framework runs it on unload. Moreover, the built-in
registration APIs are already effects: ctx.on(event, listener) removes the listener on unload, ctx.plugin(child) disposes the
child with its parent, and service registrations (such as ctx.tools.register(...)) attach their returned disposers to the calling plugin
automatically. So the cases where you actually hand-write ctx.effect() are rare — most of the time you just register, and the undo is free.
There is an ordering trap worth flagging, and the docs put it in bold: disposers start in reverse registration order, but multiple async disposers run concurrently. If your teardown steps must run in strict sequence, put them in one disposer and await them there. This is the engineering face of the "undo chain": last-registered, first-torn-down, like a stack of plates you take from the top — the concrete landing of "revertible."
One level deeper sits the fiber. Every loaded plugin instance owns a fiber (a runtime handle) that moves through this state machine:
PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
↘ FAILED - PENDING — declared, but an
injected service is not available yet. This is the runtime face of "reactive coeffects": a plugin waiting on its dependencies. - LOADING / ACTIVE —
applyis running / has finished. - FAILED —
applyor config validation threw. - UNLOADING / DISPOSED — disposers are running / everything is torn down.
The tutorial adds a practical diagnostic: if a plugin "prints nothing," the answer is usually that it is stuck in PENDING — waiting on
a dependency that never arrived. And fiber.dispose() resolves only after all of the plugin's cleanup (including async disposers) has
finished, recursively unloading every child plugin it mounted. Together these two close the loop on "unplug at any time": loading is declarative
waiting, unloading is recursive, reverse-ordered, and awaitable.
6. How DSH uses Cordis: the plugin tree, seams, and "no privileged kernel"
With that foundation, DSH's architecture doc (docs/architecture.md) reads smoothly. Its opening line: Cordis is the framework underneath
dsh — "plugins contribute services, typed events, and reversible side-effects to a shared context," and every part of the product is a plugin — the
model adapter, tool registry, session log, and the agent loop itself — so every part can be replaced from config. There is no privileged kernel
to patch: extending dsh means mounting plugins alongside other plugins.
A running dsh is a plugin tree, composed from layers applied in order at startup:
| Concept | Role |
|---|---|
| Profile | A named assembly in the Harness home: lists its bundles, holds out-of-tree plugins, and keeps the user's own cordis.patch.yml; web and headless ship as templates |
| Bundle | The distribution format for a Cordis config entry plus its mounting code; upper layers can always patch it |
| dsh-base | Each profile's first layer: model adapters, tools, persistence, sandbox & approval policy, settings, credentials, telemetry |
| Patch layers | profile patch → home-level patch → any --patch overlay, applied in order |
The layers apply over an empty entry list in this order: each bundle in the profile's listed order, then the profile's cordis.patch.yml,
then the home-level one, then any --patch overlays. A patch locates an entry by id and replaces its entire config, or inserts a new entry.
You can print the exact config tree your machine would boot with via dsh --profile web --dump-config — and any entry it prints can be
replaced by your own patch. That is what "composable" looks like on the user side: not "edit the source," but "stack one more patch."
Core packages contribute services (ctx keys) to this tree, per the architecture doc's table:
| Package | Responsibility | ctx key |
|---|---|---|
| core/session | Append-only SessionEvent log and in-memory store | ctx.sessions |
| core/system-prompt | Prompt-section and tool-schema assembly | ctx.systemPrompt |
| core/tools | Scoped tool registry + gated execution pipeline | ctx.tools |
| core/agent | The Agent interface, live-agent registry, and agent/* events | ctx.agents |
| core/agent-loop | The default driver implementing the Agent interface | ctx.agentLoop |
| llm/llm | Message/streaming vocabulary and the adapter seam | ctx.llm |
The most illuminating word in this table is seam. DSH defines a seam as "a swappable capability" with three roles: a
Service Definition (declares the interface, owns its own ctx.<key> and vocabulary type), one or more
Service Providers (implement it), and one or more Consumers (typically model-facing tools) that inject it. Take
packages/shell: dsh-shell is the Service Definition, dsh-bash-local / dsh-bash-sandbox are the
providers, and dsh-tool-bash is the Consumer. The filesystem and process providers share the same "execution world," so pointing their seam
at a remote sandbox moves Bash, PTY, and LSP along with it — no provider-specific fork required. This is the mechanism by which
"swapping one provider changes the whole product," and why DSH can say "the model is a plugin" so casually.
Events come in three kinds — the architecture doc stresses "events are extension points": session events are persistent facts appended
to the log and broadcast via session/event; agent events (agent/*) carry the live Agent, for observing or
intercepting in-flight work; capability events attach policies and adapters to seams like fs/*, tools/*,
telemetry/* without import cycles. And one invariant is hammered repeatedly: "model-visible ⇔ recorded" — everything that
reaches a model request must be reconstructable from the session log, enforced by a runtime assertion. Context compaction uses replacement events; the
original history is never deleted. The point of the invariant: it makes "reversible" apply not just to plugin assembly but to every step of the agent's
behavior — debugging, auditing, and replay all share the same event stream.
7. The four run modes in detail: Standard / PTC(Code) / Minimal / Creation
With Cordis's "everything is a plugin" base, "run modes" fall out naturally: they are not hardcoded branches but four shipped agent presets.
The previous post defined a preset as "the plugin assembly an agent runs"; this one digs into each preset's config files
(apps/cli/config/agent-presets/*/preset.yml and agent.cordis.yml) to see exactly what each turns on and off. A summary table
first, then one by one.
| Directory | Display name | Description (verbatim from preset.yml) |
|---|---|---|
standard | Standard | The full coding agent: file editing, shell, file & web search, skills, plan, goals, subagents, and workflows. |
code | PTC / Code | Everything in standard, plus presenting tools via the Code Mode SDK so the model composes multi-step operations with a TypeScript program. |
minimal | Minimal | A two-tool coding agent providing only persistent bash and str_replace_editor. |
cordis | Creation | For authoring custom agent presets: everything in standard, plus runtime inspection, plugin experimentation, and preset-authoring guidance. |
Note an easily missed but information-dense detail: the four presets' directory names are standard / code /
minimal / cordis, while the display names are "Standard / PTC / Minimal / Creation." So "PTC" is the Chinese-facing display
name; its directory id is code and its English mechanism name is Code Mode. The repo does not expand the "PTC" abbreviation
itself (no full name anywhere), so this post describes the mechanism and declines to invent an expansion. Likewise, "Creation" mode's directory id is
cordis — it is named after the framework itself, which is its own clue.
Standard: a "mounted once, joined many" full coding agent
The standard preset's agent.cordis.yml opens with: it is the full coding agent, "mounted once per process." That "once" is deliberate — it
is a standing scope: the roster mounts it once, and every session naming it joins by scope parentage, so the tools and
prompt sections registered here cover each joined agent while a session's own state stays keyed per Session/Agent inside the plugins. This is the
scope primitive from earlier: a contribution is either global (visible to all agents) or scoped (belonging to exactly one scope key).
What it turns on, from the config rows: bash / pwsh shells, filesystem (fs + fs-search), background jobs, skills, goals, plan mode, context compaction,
delegation and workflows (subagent / subagent_fork / workflow / ralph), ask_user, todo, and web search. Two rows marked disabled: true are
especially telling: tool-subagent-codex and tool-subagent-claude-code. DSH ships with "delegate a subagent to Codex / Claude
Code" product providers built in, but off by default; copy the preset, remove disabled from a row, and only your agent gains the
"outsource a subagent to a rival product" capability. The most concrete footnote to "everything is a plugin": even "outsource to a competitor" is a
togglable plugin row.
PTC / Code: let the model write a TypeScript program and collapse N round trips into one
Code mode is the most mechanism-driven of the four. Its agent.cordis.yml opens clearly: everything in standard is unchanged; the only
addition is a tool-presentation row (@deepseek-ai/dsh-agent-tool-presentation, mode: code). The effect:
instead of one tool call per action, the model writes a TypeScript program against a generated SDK and run_code executes it
— so a sequence that would be five round trips becomes one.
A very "Cordis" detail is worth isolating: the config comment stresses that the registry itself stays on the host plane — the agent loop's scheduler and the API proxy's presenters are its consumers; what this preset owns is only the presentation of that registry for this one agent. Native sessions run beside it in the same process, each seeing its own catalog. In other words, Code mode does not change "capability"; it changes "presentation of capability" — re-presenting N fine-grained tool calls as one "programmable composition" interface. It overlaps with how Claude Code calls tools one by one and how Codex lets the model write scripts, but DSH makes it a preset toggle rather than an intrinsic product shape.
Minimal: two tools, built to "measure the model fairly"
Minimal is the densest of the four, because its reason for existing has nothing to do with "product polish" and everything to do with fair
benchmarking. Its first comment line: a fixed-prompt, two-tool coding-agent composition. Its persona is the complete
system prompt (complete: true) — global identity, Web orientation, tool guidance, and any later assembly listeners cannot add more
prompt text; runtime context snapshots are suppressed (includeRuntimeContext: false); the model composes only two
tools — persistent bash and str_replace_editor; and context compaction is absent.
Why is that combination "for benchmarking"? Because when you compare two models' agent ability, any difference in prompt engineering, tool richness, or compaction strategy becomes a confound. Minimal pins all of them: the same two-sentence prompt, the same two tools, the same no-compaction context — so the only remaining difference is the model itself. The previous post noted that "DeepSeek-V4-Flash GA uses DeepSeek Harness's minimal mode as its test framework"; now the mechanism is clear: DeepSeek uses it to keep the "execution environment" variable controlled in its own agent benchmarks — which also means the official scores always measure "the model in this controlled environment," not raw model ability. This site has already flagged that as "relay but treat as unverified"; this post adds the "why" at the mechanism level.
Creation (cordis): an agent that can read and write its own runtime
Creation is the most radical and self-referential of the four. Its agent.cordis.yml opens: the standard coding agent, plus the ability
to read and write the runtime it is running in. Its stated purpose, in the docs' own words: so a person can ask an agent to author another
agent. Everything in standard is unchanged; what is added is a self-referential Cordis toolset (dsh-tool-cordis, whose
cordis_mount evaluates model-written JavaScript against the live runtime), a skill that teaches composition authoring
(editing-cordis-compositions), and a persona explaining the "two planes."
"Two planes" deserves expansion, because it is the key mental model for the whole DSH architecture: the HOST plane holds the registries
themselves and everything shared across sessions — persistence, the sandbox and approval stack, the model route, the subagent registry and its backends;
the AGENT PRESET plane holds what one session contributes to those registries — its tools, its persona, its prompt sections. A row that
publishes a service belongs on the host plane, or inside an isolate realm when the preset genuinely owns that service and nothing outside one
agent reads it. That "which row goes on which plane" distinction is exactly what the skill teaches the model.
The more serious part is the security boundary. The Creation preset's config header carries a TRUST warning in capitals: cordis_mount
evaluates model-written JavaScript against the live runtime, and a composition this agent writes becomes a preset other sessions mount —
"treat a session on this preset as shell access." In other words, Creation mode is not a sandbox; it is a trust boundary.
Letting a model modify its own runtime assembly under controlled conditions, in a freshly released v0.1, is aggressive — and it is the natural extension
of the "agent as infrastructure" line; the docs also explicitly warn of compatibility-breaking changes ahead, so treat it carefully in production.
8. Conclusion: what it all actually means
Piece the four modes together with Cordis's mechanics and DSH's full picture snaps into focus: it is not "an agent product with a plugin system" — it is "a runtime that assembles agent products out of a plugin system." Standard / PTC / Minimal / Creation are four presets of the same Cordis assembly: Standard is the full shape, PTC swaps the tool presentation, Minimal pins every variable to measure the model, and Creation turns "edit the runtime" itself into a tool. All four differ only in which plugin rows are on / off / swapped — not a single hardcoded branch.
For this site's readers, three practical takeaways. First, the model is a plugin, a seam — so you can point DSH's ctx.llm
at a mainland China relay's OpenAI-compatible endpoint and drive it with DeepSeek V4 Pro / V4 Flash. This is the same thing as the previous post's
DEEPSEEK_BASE_URL / Web UI / settings.yaml wiring, except now you know why it is so smooth: you are swapping not a hardcoded "model" but a
reversibly registered adapter plugin. Second, minimal mode is the "controlled variable" in official benchmarks — so when you see a
"DeepSeek crushes X" headline, first ask: in which mode, under which harness? Third, Creation mode is a trust boundary, not a sandbox —
at v0.1, don't let it run loose in a production environment holding real credentials.
A closing honesty note: the formal definitions of "revertible effects / reactive coeffects" are relayed from the paper and the official primer, not an
independent formal re-verification by this site; and DSH's local modifications to Cordis (fiber hardening, transactional hot-reload) come from the vendor
directory's "local modifications log," i.e., the official self-account. Both are worth checking against the source before you lean on them. Beyond that,
every description of the four presets in this post is drawn directly from the repo's preset.yml and agent.cordis.yml text and
comments — safe to treat as "the facts of the current version."
Put together, this means
- Why DSH can let "everything be a plugin" → the answer is Cordis's Revertible Effects (temporal) + Reactive Coeffects (spatial): registrations carry disposers, unloading undoes in reverse order, and dependencies must be ready before activation.
- How the model becomes a swappable plugin → the key is the
ctx.llmseam (Service Definition + Provider + Consumer) and the profile/bundle/patch layering. - The four modes → standard (full), code/PTC (Code Mode: the model writes a TypeScript program), minimal (two tools, fixed prompt, for benchmarking), cordis/Creation (reads and writes its own runtime; a trust boundary).
- Two caveats → official agent scores include the "minimal mode" controlled-environment variable, so wait for third-party re-tests; Creation mode is a trust boundary, not a sandbox, and v0.1 carries breaking-change risk.