DeepSeek Harness Bluebook
Developer Guide

Cordis Primer

The core concepts of the Cordis plugin framework underneath DeepSeek Harness: plugins, ctx, services, events and reversible effects

Cordis is the vendored plugin framework underneath DeepSeek Harness. Before writing a harness plugin, learn these core concepts; the full service/event reference lives in each capability subsystem's generated documentation.

Five core ideas

  • A plugin is an object that implements Service. It can be a function with optional inject and apply(ctx) fields, or a Service subclass whose lifecycle Cordis mounts into the current context.
  • A context is a repository of services. A service claims a stable ctx.<key> such as ctx.tools, ctx.llm, or ctx.sessions; other plugins find services by key instead of importing a concrete implementation.
  • Declare service dependencies with inject. A plugin that names required services waits until those services exist, so load order is expressed through service requirements rather than manual boot sequencing.
  • Typed events for communication. Services declare event names through TypeScript declaration merging, then dispatch them as emit, waterfall, parallel, or serial depending on whether listeners observe, wrap, fan out, or run in order.
  • Registrations are reversible effects. Prompt sections, tool schemas, adapters, providers, and listeners are installed through ctx.effect() or ctx.on() so reload and teardown unwind them predictably.

Dispatch modes

Every event has one of the following dispatch modes and can only be dispatched by the corresponding method.

ModeAwaited?Dispatch orderHas return value?
emitNolisteners observe in registration orderNo
waterfallNolisteners observe in registration orderYes
parallelYesall listeners observe the event in parallelNo
serialYeslisteners observe in registration orderYes

The dispatch mode is part of an event's public contract.

Waterfall semantics

ctx.waterfall is around-middleware. A listener receives (...args, next); calling next() brings the downstream result back to the current layer, which may wrap it before returning. Returning without next() short-circuits the chain.

Cooperative listeners usually mutate a shared request or decision object, then delegate. A listener that only annotates or observes must delegate; a policy listener that owns the decision may return directly.

A minimal plugin

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

export const name = 'my-plugin'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(heartbeat, 5000)
    return () => clearInterval(timer) // disposer
  })
}

inject makes the plugin wait for tools to be ready; the function returned from ctx.effect() runs when the plugin unloads.

Practical rules

  • Encapsulate behavior into plugins: the tool pipeline belongs to ctx.tools, model streaming belongs to ctx.llm, and live agent loop coordination belongs to ctx.agents.
  • Prefer events for interception and policy; prefer service methods for direct capability calls.
  • Every registration should have a disposer, either returned from ctx.effect() or provided by a Cordis helper. If teardown order matters, keep the related work in one effect.

Next steps

  • Use this model to read the Architecture overview.
  • When you want hands-on examples, see the Cookbook.

On this page