Back to all posts

Cordis Explained: How DeepSeek Harness's Plugin Framework Works

Cordis is the plugin framework behind DeepSeek Harness: what revertible effects and reactive coeffects mean, and why it matters for self-improving agents.

Lucy13 min read
Abstract painterly oil-wave illustration in navy, teal, and white, evoking a ship replacing its own planks at sea
On this page

There's an old thought experiment called the Ship of Theseus: a ship gets its planks replaced one by one over the years until nothing original remains, and philosophers argue about whether it's still "the same ship." The more interesting version of that question isn't asked about a ship in dry dock — it's asked about a ship still at sea, replacing its own planks mid-voyage, without sinking and without stopping.

That's roughly the engineering problem sitting underneath Cordis, the plugin runtime that DeepSeek Harness (DSH) — DeepSeek's open-source agent harness, released August 13, 2026 — is built on. This piece is about Cordis specifically; for the product-level view of DSH itself — its four run modes, the append-only session log, how to try it — see our DeepSeek Harness overview.

What is Cordis?

Cordis describes itself as "a meta-framework of spatiotemporal composability." By itself it doesn't give you any agent capability — it can't search the web or write code — it gives you a runtime protocol for components (called plugins) to register capabilities into a shared environment, declare what they depend on, and unwind cleanly when they're removed.

DSH's own architecture docs put its role plainly:

"Cordis is the framework under dsh: plugins contribute services, typed events, and reversible effects to a shared context. Every part of the product is a plugin, including the model adapter, the tool registry, the session log, and the agent loop itself, so every part is replaceable from configuration."

"There is no privileged core to patch: you extend dsh by mounting a plugin beside the others, and registrations are effects that unwind when their plugin unloads."

docs/architecture.md, DeepSeek Harness

One detail that's easy to miss, and worth knowing before anything else: Cordis isn't something DeepSeek invented for this release. It's an independent open-source project by developer Shigma (Yifan Shi), and it has been the plugin kernel behind Koishi, a cross-platform chatbot framework Shigma has maintained since 2019. Cordis's defining idea — that unloading a plugin should completely undo everything it did — started life as a Koishi design principle years before it had a name like "spatiotemporal composability." DeepSeek vendored Cordis into DSH and, alongside the harness's launch, published a preprint formalizing the underlying theory: "A Programming Paradigm for Spatiotemporal Composability."

That lineage is the most interesting fact in this whole story. Cordis wasn't designed with AI agents in mind at all — it was designed so a chatbot framework's plugin ecosystem wouldn't slowly rot as thousands of community plugins were installed, updated, and removed over the years. DSH is betting that the same property is exactly what a long-running, self-modifying agent needs.

Cordis at a glance

The theory: two kinds of composability

The paper splits "can components be safely composed at runtime?" into two orthogonal problems, and gives each one a precise, formal answer.

Temporal composability is about time: when a component is removed, can all of its side effects be completely reverted? If a plugin registered event listeners, opened connections, or mutated shared state, does taking it out actually undo all of that, or does it leave something behind?

Spatial composability is about relationships: can components declare what they depend on, and have the runtime reactively manage those dependencies as things change? If plugin B depends on a service from plugin A, the system needs to guarantee B loads only after A is ready, unloads before A stops, and never starts at all if A failed.

The paper's own abstract names the two mechanisms it uses to solve this, lifting a pair of classical programming-language ideas — effects and coeffects — into runtime machinery:

"We formalize revertible effects, in which every context transformation carries an inverse that the runtime tracks. We formalize reactive coeffects, in which each change of the context notifies a component against its coeffect specification. We unify the effect context and the coeffect context into a single context type, which constitutes a programming paradigm."

A Programming Paradigm for Spatiotemporal Composability

A quick definition, if PL theory isn't your daily vocabulary: an effect is anything a piece of code does besides returning a value — writing to a database, registering a listener, mutating shared state. A coeffect is the dual idea: not what code does to its environment, but what it needs from it. Cordis's contribution is making both first-class, trackable, and — critically — automatically reversible.

In practice:

  • Revertible effects solve temporal composability. Every context transformation a plugin makes carries an inverse, tracked automatically by the runtime. This is the mechanism behind ctx.effect(): whatever you register through it, Cordis knows how to unregister.
  • Reactive coeffects solve spatial composability. Every plugin declares the external context it needs (via inject), and the runtime notifies it whenever that context changes, rather than the plugin having to poll or assume its dependencies are static.

The paper unifies both into a single Context type — one object that is simultaneously where a plugin's effects get recorded and where its coeffect requirements get resolved. That unification is what lets Cordis treat "install a plugin," "a dependency changed," and "remove a plugin" as instances of the same underlying operation instead of three separate special cases.

There's a third property worth calling out: confluence. However a given configuration was reached — install then uninstall, a failed update that got rolled back, a dependency that disappeared and came back — if the final desired configuration is the same, the system should converge to the same stable, explainable state. In plain terms: a system that's been hot-patched fifty times shouldn't behave differently from one that was configured correctly the first time, and it shouldn't be quietly dragging around listeners from plugins that were "removed" three iterations ago.

From theory to API: what a plugin author actually touches

The theory shows up in a fairly compact set of concrete APIs. To make it tangible before going through each piece, here's roughly what a small Cordis plugin looks like in practice (a simplified sketch for illustration, not a verbatim doc example):

import type { Context } from "cordis"

export const name = "reminders"

// A reactive coeffect: this plugin declares what it needs, and Cordis
// won't run `apply` until both services are actually available.
export const inject = ["database", "logger"]

export function apply(ctx: Context) {
  const timer = setInterval(() => {
    ctx.logger.info("checking reminders...")
  }, 60_000)

  // A revertible effect: hand Cordis a cleanup function, and it will
  // be called automatically when this plugin unloads or hot-reloads —
  // no manual teardown wiring required elsewhere in the codebase.
  ctx.effect(() => () => clearInterval(timer))

  ctx.on("message", async (session) => {
    const due = await ctx.database.get("reminders", { channelId: session.channelId })
    if (due.length) await session.send(`You have ${due.length} reminder(s).`)
  })
}

Three lines are doing the conceptual heavy lifting here: inject (coeffect declaration), ctx.effect() (revertible effect), and ctx.on() (a typed event listener, itself registered as a revertible effect under the hood). Everything below is a closer look at the APIs those lines are built on.

Context

Context (ctx) is the object every plugin interacts with — the shared container for services, events, and lifecycle operations, structured as a tree of root and child contexts.

  • ctx.extend(meta?) creates a child context that inherits (and can shadow) the parent's properties.
  • ctx.isolate(name, label?) gives a specific service its own isolated scope.
  • ctx.intercept(name, config) injects service-interception config for a child context's plugins.
  • ctx.get(name, strict?), ctx.set(name, value), and ctx.provide(name, value) are the low-level service read/write/register operations.
  • ctx.accessor(name, options) and ctx.mixin(name, mixins) let a service expose computed properties or members directly on ctx.

Context is a proxy — plain property reads go through service resolution — and scoping operations never mutate a parent; they always produce a new child. ctx.root, ctx.events, ctx.logger, and ctx.registry are the shared handles you'll reach for most often.

Fiber

A fiber is the runtime representation of a loaded plugin instance: its lifecycle state, its validated config, and the effects it has registered. Every context has an owning fiber (ctx.fiber), and ctx.effect() delegates to it to register cleanup-aware side effects — this is where "revertible effects" actually gets implemented.

PropertyPurpose
uidUnique id in the registry; the root fiber is 0
ctxThe context the plugin runs in (extended from its parent)
configThe plugin's validated configuration
stateCurrent lifecycle phase
storeSnapshot of service implementations at load time

Lifecycle methods: dispose() unloads the plugin and waits for cleanup, restart() reloads it, update(config) validates new config before restarting, and await() waits for startup work and re-throws startup errors.

Events

Five dispatch patterns cover different propagation semantics:

  • emit(name, ...args) — synchronous, fire-and-forget.
  • parallel(name, ...args) — runs all listeners concurrently, resolves once every one settles.
  • serial(name, ...args) — awaits listeners in order until one returns early.
  • bail(name, ...args) — the synchronous counterpart to serial.
  • waterfall(name, ...args) — middleware-style: each listener gets (...args, next), and can transform the value, short-circuit, or pass it on.

Listeners register via on(name, listener, options?) (returns a cleanup function, owned by the registering fiber) or once(name, listener, options?), with prepend and global options available on both.

Plugins and the registry

  • ctx.plugin(plugin, ...args) loads a plugin (function, class, or { apply() } object), validates its config against a schema, and returns a fiber.
  • ctx.inject(deps, callback) — the coeffect mechanism in practice — runs a callback once the requested services are available, and automatically reloads it if those services change. Dependencies can be an array or a name-to-config map.

The registry tracks every plugin's fiber so it can unload or reload it as service availability shifts — this is spatial composability, made concrete.

Service

Service is the base class for exposing named APIs on ctx. A subclass calls super(ctx, name), which registers it as ctx.<name> immediately and auto-removes it when the owning fiber terminates. Symbol keys cover lifecycle and extension hooks: Service.init (post-construction setup), Service.check (an availability predicate for ctx.provide()), Service.invoke (call the service directly, e.g. ctx.logger()), Service.extend, Service.tracker, and Service.resolveConfig/Service.config.

The inherited layer

Beyond harness-specific services, every plugin sees a common layer that Cordis itself provides: event dispatch, plugin management, effect, low-level service access, context derivation, runtime handles (root/scope/fiber), timing utilities (timer/interval/timeout/throttle/debounce), and system access (loader, hmr), plus roughly fourteen inherited events covering plugin lifecycle, state changes, service binding, config updates, file watching, and process exit. Keeping this generic and inherited is what lets DSH's own docs stay focused on harness vocabulary — tools, sessions, agents — instead of re-explaining plugin mechanics every time.


Why a chatbot plugin kernel ended up under an agent harness

This is the part that makes Cordis more than an implementation detail.

In her July 2026 post "Harness Engineering for Self-Improvement," OpenAI researcher Lilian Weng argues that the near-term path to recursive self-improvement runs through the harness, not the model weights. The target of optimization keeps moving outward: from instruction prompts, to structured context, to workflow design, to the harness code itself, and eventually to the optimizer code that proposes harness changes. A self-improving harness, in her framing, works in a loop — mine failed trajectories for recurring weaknesses, propose a bounded edit, validate it against held-out tasks so it doesn't regress anything, and only then merge it.

Read that loop carefully and one requirement jumps out — the exact one Cordis was built to satisfy. An agent that edits its own tools, prompts, or workflow cannot afford to restart to apply the change, and if a change turns out to be bad, the runtime needs to unwind it completely rather than leave the system in a half-mutated state. That's temporal composability.

And when one part of the harness changes — a tool gets replaced, a memory backend gets swapped — everything that depended on it needs to reconnect correctly, without touching the parts that didn't change. That's spatial composability. Confluence is what keeps a harness that's been through dozens of self-directed edits from turning into an unexplainable pile of dead listeners and stale connections.

Cordis doesn't decide whether a given self-modification is a good idea — that's still down to evaluators, held-out validation, and (per Weng's own caution about self-editing systems) keeping components like the evaluator and the permission system outside the self-editing loop entirely. What Cordis guarantees is the more basic property underneath all of that: whatever gets changed, changes cleanly, and can be changed back.

Who should care about Cordis

  • engineers building plugins for DeepSeek Harness (tools, model adapters, session or policy logic)
  • anyone designing a service-oriented, dependency-injected plugin architecture that needs to survive runtime reconfiguration
  • people thinking about self-improving or self-modifying agent systems, where "can the system safely edit itself while running" is a load-bearing question, not a nice-to-have
  • readers curious how a plugin kernel built for a 2019 chatbot framework ended up underneath a frontier lab's agent harness

It's less relevant if you're only using DSH as an end user — Cordis is infrastructure for people extending the harness, not part of its user-facing surface.

Final thoughts

Cordis is a reminder that not every idea behind "self-improving agents" has to be invented from scratch. In this case, it's a plugin system whose core promise — that removing a component undoes everything it did — was refined for years inside a community chatbot framework, formalized into a paper, and pointed at a much bigger problem. Whether or not DSH itself becomes the standard agent harness, the underlying question Cordis answers — how does a running system replace its own parts without falling apart — only gets more relevant as more of the "harness" becomes something the agent edits itself.

FAQ

What is Cordis?

Cordis is a "meta-framework of spatiotemporal composability": a plugin runtime where components declare dependencies, register reversible side effects, and can be loaded, unloaded, or hot-reloaded without restarting the system. DeepSeek Harness vendors it in as its underlying architecture.

Did DeepSeek create Cordis?

No. Cordis is an independent open-source project by developer Shigma, and has been the plugin kernel behind the Koishi chatbot framework since 2019 — well before DeepSeek vendored it into DSH and co-published a formal paper on its design.

What's the difference between temporal and spatial composability?

Temporal composability is whether a removed component's side effects can be completely reverted (handled by revertible effects). Spatial composability is whether components' declared dependencies are reactively kept correct as the system changes (handled by reactive coeffects).

What are the five event dispatch patterns in Cordis?

emit (fire-and-forget), parallel (concurrent, awaited), serial (sequential, can bail early), bail (synchronous version of serial), and waterfall (middleware-style chaining via next()).

What is a Fiber in Cordis?

The runtime representation of a loaded plugin instance — its lifecycle state, validated config, and registered effects — with methods like dispose(), restart(), update(), and await().

Is Cordis specific to DeepSeek Harness or JavaScript agent frameworks?

No — it's a general-purpose, MIT-licensed TypeScript/Node.js framework (cordis on npm) with no agent-specific assumptions built in. DeepSeek Harness is simply its highest-profile adopter to date, alongside Koishi.

Why does a plugin framework matter for recursive self-improvement?

Because a self-improving harness edits its own tools, prompts, and workflows while still running. That only works safely if changes can be applied without a restart and fully undone if they don't pan out — which is exactly what Cordis's revertible effects and reactive coeffects are designed to guarantee.

Source

Filed under

CordisDeepSeek HarnessSpatiotemporal ComposabilityRecursive Self-ImprovementPlugin Architecture