Skip to main content
All posts
Development

Cordis Explained: The Plugin Framework Behind DeepSeek-Harness

What Cordis is, where it came from, and how its Context, Service, and Fiber lifecycle primitives power DeepSeek-Harness's everything-is-a-plugin architecture.

Cordis is the plugin framework DeepSeek-Harness (dsh) is built on — a general-purpose plugin-oriented programming paradigm that originated in the cordiverse/Koishi ecosystem, not something DeepSeek wrote from scratch for dsh. Every dsh feature, from a single tool to the entire Web UI, is a Cordis plugin registered against a Context. Understanding Cordis's handful of primitives — Context, Service, effect, and the Fiber lifecycle state machine — is what makes the rest of dsh's "everything is a plugin" design legible rather than mysterious.

Where Cordis came from

Cordis predates dsh. It's the plugin core of Koishi, a chatbot framework, packaged as a standalone, domain-agnostic dependency-injection and plugin-lifecycle library under the cordiverse organization. DeepSeek adopted it as dsh's foundational framework rather than building a bespoke plugin system — which is why dsh's plugin docs reference Cordis concepts (Context, Service, inject) directly rather than wrapping them in dsh-specific terminology.

The core primitive: Context and three plugin shapes

Every Cordis plugin is a module that registers capabilities against a Context object, ctx. dsh's own docs give three equivalent shapes:

// 1. Function form — the common case
import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-plugin'

export function apply(ctx: Context) {
  // Register capabilities here.
}
// 2. Object form
export default {
  name: 'my-plugin',
  inject: ['tools'],
  apply(ctx: Context) {
    // ...
  },
}
// 3. Class form — when the plugin itself provides a Service to others
export default class MyService extends Service {
  static inject = ['tools']
  constructor(ctx: Context) {
    super(ctx, 'myService') // mounted as ctx.myService
  }
}

All three are interchangeable ways of saying the same thing to Cordis's loader; the class form is specifically for plugins that want to expose a reusable service other plugins can inject and call, not just run side-effecting setup code.

Services and dependency injection

A Service is a capability one plugin exposes to others, mounted at ctx.<serviceName>ctx.tools, ctx.llm, ctx.subagents are all Cordis services under the hood. Consuming plugins declare what they need with inject: ['tools']; Cordis won't call that plugin's apply() until every injected service is actually available, and will automatically unload the consumer if a required service later disappears (then reload it once the service comes back). For services that are nice-to-have but not required, ctx.get('metrics') does an optional lookup instead of a hard dependency.

By default, though, a given service name resolves to one shared instance across the whole plugin tree — the next section covers the mechanism for breaking out of that when you need per-group configuration.

Effects: cleanup without writing dispose logic

ctx.effect() is Cordis's answer to "this registration needs teardown when the plugin unloads":

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => console.log('heartbeat'), 5000)
    return () => clearInterval(timer) // called automatically on unload
  })
}

Everything registered through ctx — event listeners, ctx.tools.register() calls, effects — is torn down automatically when a plugin unloads. You don't write a manual dispose() method; Cordis tracks what a plugin registered and reverses it.

The Fiber lifecycle

Cordis models a plugin's life as a small state machine (dsh's framework docs call the unit a "Fiber"):

PENDING → LOADING → ACTIVE
                 ↘ FAILED
ACTIVE → UNLOADING → DISPOSED

A plugin sits in PENDING/LOADING until every inject-declared dependency is satisfied, then moves to ACTIVE and its apply() runs. If a dependency later disappears, the plugin transitions back out of ACTIVE through UNLOADING to DISPOSED, and re-enters the cycle if the dependency comes back. This state machine is also what makes hot-module reload work: editing a plugin's source with @deepseek-ai/cordis-plugin-hmr attached triggers unload-old → load-new → re-run apply(), using the exact same transition path a dependency-loss/recovery cycle would use.

isolate: giving different plugin groups their own service instance

By default, a Cordis service like ctx.tools or a shared Bash executor is one process-wide instance that every consuming plugin shares. isolate breaks that assumption when you need it: it lets you scope a service to a subset of plugins so they get their own instance rather than the global default. dsh's docs give a concrete case for this — giving different groups of plugins their own Bash executor, each configured with a different timeout, instead of forcing every plugin in the profile to share one timeout value. This matters in practice whenever two capabilities that nominally use "the same service" actually need different runtime behavior depending on which part of the plugin tree is calling it.

Capability seams: Cordis's answer to "replaceable capability"

A capability seam is the naming dsh's glossary gives to the complete unit built from these primitives: one Service Definition (a Cordis Service abstract class, e.g. ShellExecutor) plus one or more Service Providers (concrete implementations) plus one or more Consumers (plugins that inject it). The packages/shell package is the canonical example: dsh-shell defines the seam, dsh-bash-local/dsh-bash-sandbox are providers, dsh-tool-bash is the consumer that exposes it as a model-facing tool. This is the atomic modeling unit underneath dsh's broader "everything is a plugin" claim — a topic covered in full in our architecture guide.

The academic debate around Cordis, briefly

A companion research write-up describing Cordis's formal model circulated alongside dsh's launch and drew some pushback in developer discussion threads — critics characterized it as dressing up ordinary engineering conventions in programming-language-theory notation ("metatheory cosplay"), while others argued the underlying framework design has real, demonstrable engineering value independent of how the paper frames it. That debate is about the paper's academic presentation, not about whether Cordis works as a plugin framework in practice — it's tangential to actually building on top of it, which is what the rest of this article (and the plugin development primer) covers.

FAQ

Is Cordis specific to DeepSeek-Harness?

No — it's a general-purpose plugin framework from the cordiverse/Koishi ecosystem that dsh adopted, not something built exclusively for it.

Do I need to understand Cordis to write a dsh plugin?

At a basic level, yes — every dsh plugin is a Cordis plugin. The good news is the core surface is small: Context, apply(), optional inject, optional Config, and ctx.effect() cover most day-to-day plugin code.

What's the difference between a Service and a plugin?

Every Service is provided by a plugin, but not every plugin provides a Service — most plugins just register tools, commands, or hooks against ctx without exposing a reusable ctx.<name> capability of their own.

How does hot-module reload actually work under the hood?

It reuses the Fiber state machine's normal unload/reload transition — @deepseek-ai/cordis-plugin-hmr watches source files and drives a plugin through UNLOADING → DISPOSED → LOADING → ACTIVE on file changes, the same path a dependency going away and coming back would trigger.

Is the Cordis paper controversy relevant to using dsh?

Not really — it's a dispute about academic framing of the underlying theory, not about the framework's practical behavior. It doesn't affect how you write or run a plugin.

Next steps

For how these primitives compose into dsh's full extension-point map, read DeepSeek-Harness architecture: everything is a plugin. For hands-on plugin code using Context, inject, and ctx.effect(), see the plugin development primer. For terminology like capability seam and Fiber, check the glossary, and browse Development & Runtime plugins for tooling built on top of this framework.