$dshubuser@dshub:~/plugins$
  • Plugins
  • Submit
  • Blog
Hands-on: Write Your First Cordis Plugin with hello.ts
2026/08/20

Hands-on: Write Your First Cordis Plugin with hello.ts

Run the shortest path from a function plugin and cordis.yml through Loader, Context, and Fiber.

Goal: create one tiny plugin, let Loader read cordis.yml, call apply(ctx), and see real output.

This article does not start with the full architecture. First make a plugin run, then inspect why these three lines are enough to enter the Cordis runtime.

01 / Create the plugin file

Terminal trace showing hello.ts becoming an active Cordis plugin through Loader

Create hello.ts in the tutorial’s temporary directory:

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

export const name = 'hello'

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

name is display metadata used by diagnostics. The real entry point is apply(ctx): Loader imports the module and passes it the current Context.

02 / Compose it with cordis.yml

Create cordis.yml beside it:

- name: './hello.ts'

The YAML file is a list of entries. Each name can be a relative path or package name; Loader mounts one plugin for each entry.

Cordis plugin shapes showing function, object apply, and Service class forms

There is no application bootstrap code in this file. The plugin describes its contribution, while cordis.yml chooses which contributions enter this run.

03 / Run it and inspect the result

From the tutorial directory in DeepSeek Harness, run:

node --import tsx ../../vendor/cordis/bin.js

Expected output:

hello from my first plugin

The boot path is three steps:

  1. The single-file launcher creates a root Context and mounts Loader.
  2. Loader reads cordis.yml and resolves ./hello.ts.
  3. Cordis calls apply(ctx) and gives the plugin its Fiber lifecycle.

Entries may start concurrently; list position does not guarantee load order. If a plugin needs a service, declare inject instead of moving the row around.

04 / The other two plugin forms

A function is the smallest entry. When a plugin exposes a named service, use a Service subclass:

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

export class GreeterService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'greeter')
  }
}

export const objectPlugin = {
  name: 'object-plugin',
  apply(ctx: Context) {},
}

All three forms are mounted by Cordis. When no service is exposed, prefer a function plugin and keep the entry small.

05 / What loading failures look like

If the module resolves but apply throws, plugin loading fails explicitly and the launcher exits with failure; Cordis does not silently skip it:

export function apply(ctx: Context) {
  throw new Error('apply exploded')
}

A different case is module-resolution failure, such as a misspelled file path or package name. Loader reports the import error through the logger before it creates the Fiber, so an unresolved module never becomes PENDING. During early boot, the message may appear before a console observer is active; check name first, then inspect PENDING only after the module can load and a dependency is missing.

06 / Why this matters to DSH

DeepSeek Harness bundles are also compositions of Cordis entries. Writing a DSH plugin means writing a Loader-compatible plugin and inserting it through a profile patch layer instead of modifying the central agent loop.

Next, turn a runnable function into a named capability that other plugins can consume: Services and Context.

Sources: first-plugin tutorial, tutorial launcher, and Loader entry config.

All Posts

Author

avatar for DSH Research
DSH Research

Categories

  • Agent Infrastructure
01 / Create the plugin file02 / Compose it with cordis.yml03 / Run it and inspect the result04 / The other two plugin forms05 / What loading failures look like06 / Why this matters to DSH

More Posts

waterfall: Intercept, Wrap, and Short-Circuit with next()
Agent Infrastructure

waterfall: Intercept, Wrap, and Short-Circuit with next()

Follow a Cordis waterfall chain to see listeners delegate downstream, wrap results, or short-circuit when they own the decision.

avatar for DSH Research
DSH Research
2026/08/26
cordis.yml: Turn Plugin Rows into a Mutable Application Composition
Agent Infrastructure

cordis.yml: Turn Plugin Rows into a Mutable Application Composition

Understand Cordis entries, stable ids, groups, isolation, and patch layers as one runtime configuration tree.

avatar for DSH Research
DSH Research
2026/08/21
Cordis Inside DeepSeek Harness: Making Everything a Plugin
Agent Infrastructure

Cordis Inside DeepSeek Harness: Making Everything a Plugin

Trace dsh-base, Context services, event domains, and the tool pipeline to see how Cordis organizes a replaceable Agent runtime.

avatar for DSH Research
DSH Research
2026/08/29

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