$dshubuser@dshub:~/plugins$
  • Plugins
  • Submit
  • Blog
Cordis: Turning a Running Application into a Reversible Plugin Tree
2026/08/18

Cordis: Turning a Running Application into a Reversible Plugin Tree

Follow cordis.yml, Loader, and Context to see how Cordis composes, replaces, and safely tears down runtime capabilities in DeepSeek Harness.

The short answer: Cordis separates three runtime responsibilities: Loader composes, Context connects, and Fiber owns and reverses a plugin’s side effects.

Keep the header image as the mental model: one row in cordis.yml passes through Loader and Context and becomes a Fiber that can run, be replaced, and be removed. The four source-grounded diagrams below explain that path without turning the article into a wall of prose.

01 / How one configuration row enters the runtime

Cordis composition flow from cordis.yml through Loader, Context, inject, and an active Fiber

The flow comes from the Cordis tutorial launcher: it creates a root Context, Loader reads cordis.yml from the current directory, and configured modules are mounted into the runtime. Configuration chooses composition, the plugin contributes behavior, and Fiber records ownership of that mount.

- id: hello
  name: './hello.ts'

The plugin only needs to provide its entrypoint:

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

export function apply(ctx: Context) {
  console.log('hello from my first plugin')
}

02 / The three problems the plugin tree solves

NeedCordis actionRuntime result
Add a capabilityAdd a plugin row to compositionThe central bootstrap stays unchanged
Replace an implementationConsumers depend on service names, not filesA provider can change
Remove side effectsFiber runs disposers during unloadListeners, registrations, and resources do not linger

The point is not the word “plugin”. It is explicit runtime ownership. Finding a module is only the first step; the runtime must also know when dependencies are ready and who cleans up resources.

03 / Context is the connection layer, not a global variable

DeepSeek Harness Context connecting tools, llm, agents, and sessions services

Context provides service access and plugin registration. In DSH, ctx.tools, ctx.llm, ctx.agents, and ctx.sessions are named capabilities; a consumer does not need to import a provider’s implementation class.

When a plugin declares a service with inject, Cordis keeps it PENDING until the dependency is available and then moves it into LOADING. Readiness comes from the dependency graph, not accidental configuration order.

04 / Swap the provider, keep the consumer

Cordis replacing local and E2B shell providers behind the same shell service name

The consumer declares only the capability name:

- id: terminal-tool
  name: './terminal-tool.ts'
  inject: ['shell']

Composition can point shell to a local implementation or an E2B implementation. The consumer still depends on the same service name. This is one foundation for DSH profiles.

05 / HMR works because Fiber is reversible

Cordis Fiber lifecycle from PENDING to ACTIVE, UNLOADING, and DISPOSED

HMR is not importing the file again. It unloads the old Fiber before mounting the new Fiber. Cordis defines PENDING, LOADING, ACTIVE, FAILED, UNLOADING, and DISPOSED; each state names an observable runtime phase.

External resources attach to Fiber through an effect:

ctx.effect(() => {
  const timer = setInterval(() => console.log('tick'), 200)
  return () => clearInterval(timer)
})

The body creates the resource; the returned disposer reverses it during unload. ctx.on(), ctx.plugin(), and service registration follow the same reversible relationship.

06 / This is how DSH is organized

In DeepSeek Harness, model adapters, tools, sessions, agent management, and the agent loop enter the runtime as plugins. Reduce the model to four verbs:

  • Composition selects: a profile decides which plugins load.
  • Context connects: Service Definition, Provider, and Consumer roles cooperate by name.
  • Fiber owns: every mount has its own lifecycle.
  • Effects reverse: unload is a runtime action, not a cleanup promise.

“Everything is a plugin” therefore means that replacement points and resource ownership stay visible.

Where to go next

  1. Your first plugin: hello.ts + cordis.yml
  2. Lifecycle and effects
  3. Services and inject
  4. Composition and HMR

Implementation references: Loader launcher, Context, Plugin registry, Fiber lifecycle, and the Cordis primer.

All Posts

Author

avatar for DSH Research
DSH Research

Categories

  • Agent Infrastructure
01 / How one configuration row enters the runtime02 / The three problems the plugin tree solves03 / Context is the connection layer, not a global variable04 / Swap the provider, keep the consumer05 / HMR works because Fiber is reversible06 / This is how DSH is organizedWhere to go next

Newsletter

Join the community

Subscribe to our newsletter for the latest news and updates

[dshub] [zsh]$ dshub ls$ dshub submit$ dshub blog
-- NORMAL -- ✓ 2026 dshub