The Cordis Framework: The Plugin Kernel Inside DeepSeek Harness

Cordis is a TypeScript meta-framework of “spatiotemporal composability” that DeepSeek chose as the plugin kernel for DeepSeek Harness. It isn’t a grab-bag of utilities — it’s a runtime protocol that lets capabilities register themselves, find each other, and fully unwind when they’re removed, as laid out in the project’s own repository at github.com/cordiverse/cordis.

Architecture diagram of the Cordis context object with tools, llm, sessions, agents, fs and ui plugins attached to it

This page is published by the DSH Field Guide, an independent community resource. We are not affiliated with, endorsed by, or operated by DeepSeek, and “DeepSeek” and “DeepSeek Harness” remain trademarks of their respective owner. If you landed here looking for CORDIS from the European Commission, that’s a different thing entirely — a database of EU research programme results, not a piece of software.

What Cordis Actually Is

Cordis describes itself, word for word, as a “meta-framework of spatiotemporal composability.” It ships under the MIT License, copyrighted “2021-present Shigma,” and its repository topics read effect, framework, nodejs, plugin. A meta-framework is a framework for building frameworks: it doesn’t solve a domain problem on its own, it defines the protocol that lets other people’s pieces live together without stepping on each other.

A meta-framework, not an application framework. Cordis stays deliberately generic. It doesn’t ship a chatbot, an agent loop, or a web server — it ships the rules by which any of those things could be assembled from independent parts.

The honest caveats up front. The core cordis package on the main branch sits at version 4.0.0-rc.9, and its README says plainly that the API is unstable and can change without notice. Cordis has no documentation site of its own — the README points readers to a Cordis primer inside the DeepSeek Harness docs, and the domain cordis.js.org just serves the standard JS.ORG placeholder redirect, not a real project page.

Cordis is not CORDIS. The European Commission runs its own CORDIS — the Community Research and Development Information Service, a database of EU-funded research programme results. The name overlap is total; the overlap in meaning is zero. One is a runtime library for software components, the other is a public archive of grant outcomes.

Spatiotemporal Composability in Plain Words

Temporal composability is the ability to completely undo a component’s side effects when it’s removed: a plugin loads, leaves traces in the running system, the plugin unloads, and no traces remain. Spatial composability is the ability to declare dependencies and react to them: a needed service shows up, the dependent component turns on; the service disappears, the component turns off. The two axes are orthogonal, and a preprint on the topic treats them as separate problems before combining them.

We identify two orthogonal dimensions of the problem: temporal composability, the ability to completely revert a component’s side effects upon removal, and spatial composability, the ability to declare and reactively manage inter-component dependencies. […] We implement these ideas in Cordis, a meta-framework of spatiotemporal composability that provides a core library with effect tracking and coeffect resolution, as well as a declarative component loader with configuration reconciliation and hot module replacement.Abstract, “A Programming Paradigm for Spatiotemporal Composability,” arXiv:2608.25512

Revertible effects and reactive coeffects

The formal side works like this: every transformation of a context carries an inverse that the runtime keeps around — that’s a revertible effect, and it’s what makes temporal composability possible. Every change to a context is checked against a component’s declared requirements, which drives whether it turns on or off — that’s a reactive coeffect, and it’s what makes spatial composability possible. The authors take the classical type-system notions of effect and coeffect and lift them into runtime mechanisms instead of leaving them as compile-time bookkeeping.

The context paradigm and why it scales

Both the effect context and the coeffect context get unified into one context type, and every effect and coeffect is routed through it — the authors call this discipline the context paradigm. That single point of mediation produces an observational equivalence: the effects of different components can interleave in different orders without disturbing each other, and the paper’s calculus of dynamic composition carries that guarantee from one component up to a whole system of interleaved components. A third, more practical property falls out of the same design — confluence, meaning the system settles into one stable state regardless of the order in which components were installed, removed, or updated.

Split comparison of the two axes: a temporal timeline where a component's traces are undone, and a spatial dependency graph where a component switches on and off

The paper itself, titled “A Programming Paradigm for Spatiotemporal Composability,” is filed under arXiv:2608.25512, listed in category cs.PL with a cross-listing in cs.SE, and carries ACM classes D.2.11, D.3.1, and D.3.3 (DOI 10.48550/arXiv.2608.25512). Its authors are Yifan Shi (Peking University and DeepSeek-AI), Wei Zhang (Peking University), and Tianyi Cui (DeepSeek-AI). Version 1 was submitted on 26 August 2026 — notably after DeepSeek Harness had already shipped publicly on 13 August 2026. The abstract page doesn’t list a page count for the preprint.

What Counts as a Plugin

Cordis’s own primer puts it simply: a plugin is any object that implements Service. That leaves three interchangeable shapes — a plugin function (export function apply(ctx)), a plain object with an apply method, or a subclass of Service. The name field is metadata only, used for diagnostics; if a plugin is mounted directly as a function via ctx.plugin(heartbeat), Cordis just calls the function and no separate apply is needed.

Three plugin shapes — function, object and class — mounted concurrently by a cordis.yml config file

The application itself is assembled by config, not by code. A loader reads cordis.yml, and every entry in it is a module specifier — a relative path or an npm package name — all of which get mounted concurrently. The order of the lines in the file means nothing; startup order is driven entirely by service dependencies, not by file position. A plugin file typically contains no startup code at all — it just describes its own contribution, and the config is what turns a pile of contributions into an application. If apply throws, the process crashes with that error; if a module simply fails to resolve (a typo in a path, say), Cordis reports it through the logger service without bringing the process down.

Plugin shapeHow it’s declaredTypical use
Functionexport function apply(ctx) {...}Small, single-purpose plugins
Object with apply{ apply(ctx) {...} }Plugins that also carry static config
Service subclassclass Foo extends Service {...}Long-lived stateful services

Context, Services and Capability Seams

ctx is the central object in Cordis: services, events, and lifecycle API all flow through it. Technically it’s a proxy — an ordinary property read gets resolved through a service resolver — and calling extend(), isolate(), or intercept() creates a child context with its own scope, without mutating the parent. The context’s own built-in services include ctx.events (its methods are mixed straight into ctx, which is where ctx.on and ctx.emit come from), ctx.logger, ctx.reflect, and ctx.registry.

The capability seam. Every swappable capability in Cordis is split into three roles: a service definition, a concrete implementation, and a consumer. A consumer asks for a name, like 'tools', instead of importing a specific implementation, so the config can swap the provider without touching any consumer code. This is where dsh harness leans on Cordis most directly: DeepSeek Harness keeps its own services at stable keys — ctx.tools, ctx.llm, ctx.sessions, ctx.agents, ctx.systemPrompt — and a filesystem or shell provider can be replaced without touching the core.

Capability seam diagram: a service definition, two interchangeable implementations, and a consumer that points only at the definition

Declaring dependencies with inject. A dependency is declared by exporting inject = ['greeter']. The plugin sits in a PENDING state until every listed service is available, so inside apply a declared service is guaranteed to exist. Tracking isn’t a one-time check either: if a service disappears while the system is running, the dependent plugin unloads, then reloads automatically once the service comes back. A soft dependency, one that shouldn’t block loading, skips inject entirely and instead calls ctx.get('greeter') at the point of use, checking for undefined.

Revertible Effects and Unloading Without a Restart

The core idea here is that registration is itself a revertible side effect. Anything registered through the Cordis API gets torn down automatically. Unmanaged resources — timers, connections, watchers — get wrapped in ctx.effect(), which returns a disposer: the body runs on load, the disposer runs on unload. Developers rarely write ctx.effect() by hand, because the built-in APIs are already effects themselves. The primer’s rule of thumb is blunt: every registration should own its own disposer.

  1. A plugin calls a registration API (ctx.on, ctx.plugin, a service registration).
  2. Cordis records the matching disposer for that registration.
  3. The plugin runs normally while active.
  4. On unload, disposers fire in the reverse order of registration.
  5. Async disposers that don’t depend on each other run in parallel.
  6. Steps that must happen strictly in sequence get bundled into one disposer that awaits each step in turn.
  7. Once every disposer has resolved, the plugin is fully gone — no listeners, no timers, no open handles left behind.

This teardown order is a direct parallel to RAII in C++ and the Drop trait in Rust: last registered, first torn down. The preprint contrasts this explicitly with the VS Code extension host, where an activated extension can’t be removed on the fly — the whole host process has to restart, and dependency tracking between extensions is rarely even used. Cordis, by contrast, can unmount and swap plugins inside a live process, which is also what its hot module replacement is built on. Reporting on the launch in The Register, Thomas Claburn picked out that same contrast as the thing that makes the design unusual rather than merely tidy.

Fibers, States and Events

ctx.plugin(...) returns a fiber — a runtime handle for one loaded instance of a plugin. A fiber moves through PENDING, LOADING, ACTIVE, UNLOADING, and DISPOSED, with a FAILED branch off to the side. PENDING means the plugin has been declared but a required service isn’t available yet; FAILED means apply or config validation threw an exception. The documentation calls out PENDING specifically as the usual answer to “why is my plugin doing nothing.” fiber.dispose() only resolves once every cleanup step, including async ones, has finished, and it recursively unloads any child plugins along the way.

Fiber state machine running from PENDING through LOADING, ACTIVE, UNLOADING and DISPOSED, with a FAILED branch

Plugins also talk to each other through typed events, and the dispatch mode is treated as part of an event’s public contract, not an implementation detail:

ModeBehavior
emitFires without awaiting, listeners run in registration order, no return value
waterfallMiddleware-style chain, each listener calls next() to continue
parallelAwaited, all listeners run concurrently
serialAwaited, listeners run in order, each can return a value
bailRuns in order until one listener returns a non-null bail value

How DeepSeek Harness Builds on Cordis

The official framing is a formula: Agent = Model + Harness. In dsh harness, models, tools, skills, sessions, sandboxes, filesystems, the run loop, orchestration, and the interface are all implemented as plugins, and Cordis itself is vendored straight into the deepseek-ai/deepseek-harness repository under vendor/cordis, published internally as @deepseek-ai/cordis. That’s a sharper break from a typical agent framework than it sounds: most agent frameworks have one hardcoded main loop plus a bundle of built-in capabilities, and adding a new capability means editing the loop, the prompt assembly, and the tool dispatch. DeepSeek Harness instead turned the main loop itself into just another plugin, per its documentation at deepseek-harness.github.io.

Profiles, bundles and patches. A running dsh instance is a plugin tree assembled at startup in layers: first the profile’s bundles, in order, then that profile’s cordis.patch.yml, then a home-level patch, then any ad hoc overlays passed with --patch. The actual tree that came out of all that layering is inspectable with dsh --profile web --dump-config, and any entry that command prints can be overridden with a patch of your own. There’s no privileged core that has to be patched from the inside — extending dsh just means hanging another plugin next to the existing ones. Four presets ship out of the box: standard, code, minimal (bash and a file editor only, meant for measuring raw model capability without extra scaffolding), and creator.

Four stacked configuration layers — bundles, profile, home and patch — resolving into the output of the dsh dump-config command

The session log as a first-class artefact. Every run is written to an append-only log — system prompts, reasoning, tool calls and their results, subagent planning, every context injection. Resume, fork, search, and replay are all built on top of that log. It’s a direct consequence of the same discipline running through the rest of the system: if the single source of truth for an interaction is an append-only event log, the conversation history is something you derive from the log, not something you store separately, as covered in the official DeepSeek Harness overview.

Where Cordis Came From

Cordis started life as the plugin kernel split out of Koishi, a cross-platform TypeScript chatbot framework that’s been under active development since 2019. The connection shows up directly in the source: @koishijs/core 4.18.11 depends on cordis ^3.18.1, credited to the same author, Shigma, under the same MIT License. Koishi’s ecosystem grew to thousands of community plugins over that time — which functions as years of empirical stress-testing for the underlying model, not a fresh hypothesis invented for agents.

Koishi runs on Cordis v3; DeepSeek Harness is built on v4, a redesign that led directly to the formalization described in the arXiv preprint. The same person credited as Shigma in the Cordis LICENSE file appears in the paper’s author list as Yifan Shi.

For a broader map of how these pieces fit together across the rest of the toolchain, the DeepSeek Harness guide collects the surrounding profiles, patches, and tool integrations in one place.

FAQ