dshseek

seek://guides/what-is-cordis

What is Cordis? (paper deep-read)

Aug 14, 2026Intermediate← All guides

TL;DR

Cordis is the plugin framework underneath DeepSeek Harness, formalized in the preprint 'A Programming Paradigm for Spatiotemporal Composability'. Temporal composability: side effects fully revert on removal. Spatial composability: dependencies are declared and reactively managed — revertible effects plus reactive coeffects, unified in one context type.

Key points

  • The paper formalizes dynamic composition along two orthogonal axes: temporal (reversible effects) and spatial (reactive dependencies)
  • Revertible effects: every context transformation carries a tracked inverse — unregistering a plugin unwinds what it did
  • Reactive coeffects: components declare what they need (inject) and the runtime resolves activation order reactively
  • Both unify into a single context type; a calculus of dynamic composition lifts the guarantees to whole systems
  • Cordis implements it: effect tracking, coeffect resolution, a declarative loader with config reconciliation and HMR — dsh's 'everything is a plugin' is this calculus in production

$ dsh –read-the-paper

Most agent frameworks treat plugins as a feature. DeepSeek Harness treats plugins as a paradigm, and backs it with something almost nobody else in this space has: a paper. A Programming Paradigm for Spatiotemporal Composability formalizes the math underneath the harness’s plugin runtime, which is called Cordis.

You can absolutely use dsh without reading it. But if you’ve ever installed two extensions that both wanted to rewrite the prompt and wondered who wins and what happens when you uninstall one — congratulations, you’ve been doing spatiotemporal composability research without the vocabulary. This guide gives you the vocabulary, then the mechanism, then a taste of the code.

Why a paper matters for a chat tool

The paper opens with an honest observation: modern software — from plugin systems to “self-evolving agent harnesses” — increasingly requires dynamic composition, yet “its formal foundations remain underdeveloped”. Translated: everyone hot-loads code, almost nobody can prove what happens when it unloads.

DSH’s bet is that an ecosystem of hundreds of independent plugins only stays sane if composition has real semantics. Not conventions. Not “please clean up after yourself”. Semantics the runtime can enforce. The paper’s contribution is splitting the problem into two clean dimensions and solving both.

Two dimensions: time and space

Temporal composability is the ability to completely revert a component’s side effects upon removal. Install a memory plugin, and it adds prompt sections, a settings page, maybe a database table. Uninstall it — do all of that vanish, or does residue haunt every session afterward? If the answer is “vanish, provably”, you have temporal composability.

Spatial composability is the ability to declare and reactively manage inter-component dependencies. A tool plugin needs a tool registry to exist before it can register anything. In most systems that’s a fragile boot order. In a spatially composable system, the plugin declares what it needs and the runtime reacts — holding it pending until the world satisfies it, activating it when it does.

The two are orthogonal: you can have undo without dependency management (editors’ undo stacks) or dependency management without undo (DI containers that never unload anything). The paper’s move is to lift both into runtime mechanisms that compose.

Revertible effects

The temporal mechanism: every context transformation carries an inverse that the runtime tracks. When your plugin registers a tool, it doesn’t just mutate a registry — it performs a tracked effect that knows how to undo itself:

ctx.effect(() => {
  const dispose = ctx.tools.register(myTool)
  return dispose // the inverse: unload unregisters the tool
})

Register three tools, a prompt section and an event listener in one effect, and teardown unwinds them in order. The primer’s rule is blunt: every registration should have a disposer. This is also what makes hot reload a feature instead of a crash — unload the old version (all inverses run), mount the new one.

Reactive coeffects

The spatial mechanism: each component declares a coeffect specification — what it requires of its context — and the runtime notifies it when the context changes to satisfy (or break) that spec. In dsh’s vocabulary, that’s inject:

export const name = 'greet-tool'
export const inject = ['tools']   // coeffect: need ctx.tools before apply()

The plugin stays PENDING until ctx.tools exists. Nobody wrote a boot sequence; the dependency is the configuration. If a provider is missing, the failure mode is a legible “this plugin is waiting for X”, not a TypeError deep in startup.

One context type

The elegant part: the effect context (where you act on the world) and the coeffect context (what you require of it) unify into a single context type. That one object — ctx in every dsh plugin — is simultaneously your capability handle and your dependency declaration surface. From there the paper builds the notion of a component and a calculus of dynamic composition, with metatheory that carries the guarantees “from a single component to a whole system of interleaved components”.

You don’t need the calculus to feel it. The reason a dsh config file reads like a list of rows, and yet boots into a coherent product, is that each row is a component in this calculus — the metatheory is what lets hundreds of them interleave without the guarantees collapsing.

The vocabulary you’ll actually use

Five ideas from the upstream primer cover day-to-day plugin work — each is the paper’s machinery wearing practical clothes:

  1. A plugin is an object implementing Service — a function with inject and apply(ctx), or a Service subclass whose lifecycle Cordis mounts.
  2. A context is a repository of services — services claim stable keys (ctx.tools, ctx.llm); you look things up by key, never by import.
  3. Dependencies go through inject — load order is derived, not sequenced.
  4. Typed events for communication — declared via TypeScript declaration merging, dispatched four ways:
    Mode Awaited Order Returns
    emit no registration order no
    waterfall no registration order yes
    parallel yes all at once no
    serial yes registration order yes
  5. Registrations are reversible effects — everything you add comes back out.

The one semantic to internalize before your first interceptor: waterfall is around-middleware. Your listener gets (...args, next); call next() to delegate, return without it to short-circuit. For single-decision events, short-circuiting is the design — a policy listener that owns the decision returns; a listener that merely observes must delegate. Getting this backwards is the classic first bug.

Twenty lines of Cordis

From the upstream tutorial’s final chapter — a model-callable tool, complete:

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'greet-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'greet',
    description: 'Greet the named person.',
    parameters: { name: { type: 'string', required: true } },
    async execute(args) { return `Hello, ${args.name}!` },
  }))
}

Every idea is in there: inject (coeffect), apply (lifecycle), ctx.tools.register (tracked effect on a keyed service). Add a second plugin that does nothing but ctx.on('tools/result', ...) and you have observation without coupling — neither plugin knows the other exists; the context connects them.

How dsh uses it

Zooming out: dsh is a pile of such plugins, composed at boot from ordered layers — bundles, then your cordis.patch.yml, then overlays. The session log, the tool pipeline, the agent loop, the Web UI: all rows in that tree, all patchable. dsh --profile web --dump-config prints the entire composition; any row can be replaced by a patch. When the map indexes a plugin, this is the mechanism it plugs into — which is why plugin conflicts in this ecosystem are tractable in principle, not just in hope.

Reading the paper itself

The preprint (draft of August 13, 2026) is short and denser than this guide: read the abstract, then the calculus section, then the metatheory. The authors ask you to cite the latest version — it’s under active revision. Start at github.com/cordiverse/paper; the PDF is in the repo.

If you want the hands-on path instead: the upstream Cordis tutorial walks all of this in seven chapters, keyless and runnable. And if you haven’t yet, read what-is-harness first for the product-level picture this paper sits underneath.

Official references