DSH plugin package in DeepSeek Harness: when, rules, checks

Plugin DevelopmentPublished 2026-10-02Author: DeepSeek Plugin Market
DSH pluginDeepSeek Harnessworkspace packageplugin developmentpackage structure
When should you add a DSH plugin workspace package in DeepSeek Harness? Layout, package.json invariants, root registration, naming, README, and checks.

Adding a DSH plugin workspace package in DeepSeek Harness comes down to three questions: when to add one, what makes a new package compliant, and how to verify it. The official docs provide a file-by-file checklist validated against the bash and adapter packages (source), and this article breaks that checklist into executable steps.

When to add a DSH plugin workspace package

Add a workspace package only when a capability must be reused or evolve independently inside the repo; a third-party DSH plugin installed from DSH Plugin Hub never lands in the packages directory. Three tests decide it:

  1. The capability is reused by several packages — if a capability only serves one plugin, keep it in that package first and split once a second consumer appears. Expect: no premature splitting.
  2. The capability must evolve as a replaceable capability — split the Service Definition, Service Provider, and Consumer into different packages when each must be swapped separately. Expect: swapping a provider does not touch the consumer, which is exactly the value of capability seams.
  3. It is an external third-party plugin — a third-party DSH plugin installed from DSH Plugin Hub is distributed externally, never enters this repo's packages directory, and is not bound by the package.json invariants here. Expect: a clear line between in-repo capability packages and external plugins.

Structure and hard constraints of a new DSH plugin package

Layout and five required files: the fixed spot for @deepseek-ai/dsh-<name>

A new package always sits under packages/<group>/<pkg>/, is named @deepseek-ai/dsh-<name>, and its minimal form contains five locations (source). The directory looks like this:

text
packages/<group>/<pkg>/
  package.json     # copy packages/core/tools, then adjust name/description/deps
  tsconfig.json    # extends ../../../tsconfig.base.json
  src/index.ts     # service default export, or a plugin (name/inject/apply/Config)
  locale/en.json   # optional display metadata meta.title / meta.description
  locale/zh.json   # translation of the same fields
  README.md        # service API, events, extension points, design notes, and Model Experience
  1. Pick a group — reuse an existing group when it matches the package role (core, llm, shell, compaction, subagent, todo, session, client/host, util, test-support). Expect: a new group is allowed, but a group is a pure container with no package.json and no source files.
  2. Write tsconfig.json — extend ../../../tsconfig.base.json, with rootDir as src, outDir as lib/types, and references including at least ../../../vendor/cosmokit and ../../../vendor/cordis; add ../../../vendor/schemastery when you use Config, and add ../../<group>/<dep> for every dsh dependency. Expect: type resolution finds every dependency.
  3. Write src/index.ts — export a service or a plugin in the name/inject/apply/Config shape. Expect: the package can be loaded by Cordis.
  4. Use explicit .ts suffixes for relative imports inside the package, for example export * from './types.ts'. Expect: the compiler rewrites them to .js in the emitted JS, keeps .ts in declaration files, and standard NodeNext/Node16 consumers resolve the sibling .d.ts.

package.json invariants

package.json fields are not a style choice but hard constraints enforced by pnpm run constraints (source). The key items are:

FieldRequirement
privatetrue
versionmatches the root package.json
typemodule
main / typeslib/index.js / lib/types/index.d.ts
exports["."]types points to ./lib/types/index.d.ts, default points to ./lib/index.js
@deepseek-ai/cordisappears in both peerDependencies and devDependencies with the same range
other dsh peer dependencieseach mirrored in devDependencies
@deepseek-ai/schemasteryplaced in dependencies (it is a runtime validator)
filesprecisely include lib/index.js, lib/types/**/*.d.ts, and package-specific runtime artifacts accepted by the gate
  1. A package that publishes ./invariant must also include lib/invariant.js; when a runtime export points into the output tree, also include lib/types/**/*.js. Expect: the published artifact is complete.
  2. Do not publish src, declaration maps, JS maps, or stale root declaration files. Expect: the smallest package size and exposure surface.
  3. A CLI application package with bin lists lib/bin.js right after lib/index.js in files. Expect: the command entry is packaged correctly.

Registering a new package in root config

Writing the code is not enough — a new package must be registered in the root config before it enters the project reference graph and the check scope (source). Only two places change:

FileChange
tsconfig.base.jsonno edit needed for an existing group; a new group needs candidate paths ./packages/<group>/*/src for the @deepseek-ai/dsh-* wildcard
tsconfig.host.json (Host packages) or tsconfig.client.json (Client packages)add { "path": "./packages/<group>/<pkg>" } to references
  1. An ordinary package belongs to exactly one aggregate, never to Host and Client at once. Expect: the reference graph has no duplicate resolution.
  2. packages/client/* packages extend tsconfig.base.client.json instead, and a client plugin package must also declare dsh.client in package.json, export ./client, and call the shared tsdown preset. Expect: the client-side build goes through one entry point.
  3. The following are covered automatically by globs or the package manifest discovery mechanism and need no manual edit: root package.json workspaces, scripts/publint-all.ts, tsdown.config.ts, .oxlintrc.json, scripts/check-workspace-constraints.ts. Expect: the new package is pulled into checks automatically.

One repo-specific exception: api/remotes uses a split because of an ordering dependency between the Host generation convention and the Client consumption convention, and new packages must not copy it.

Naming by role: topology and ctx keys

Package topology and naming must describe the current stable responsibility, because that is how others understand and replace your capability (source). Two rules:

  1. Split packages for a replaceable capability — do not put the interface, the implementation, and the consumer in one package; keep a single-purpose plugin in one package. Expect: the topology matches the tests in the first section.
  2. Names map to the current responsibility — do not name by first implementation, future extension, or Cordis base class. Use the capability name for interface packages, and add a mechanism/protocol/environment/vendor qualifier to implementation packages; use local only when same-host execution is the convention. Expect: names like bash-local, bash-sandbox, and fs-local explain themselves.

The singular/plural form of a ctx key is also a convention: use singular for engine, runtime, policy, controller, resolver, store, or a single current config; use plural for a registry or a service with several named members. The role of a class and the number of its key must match, and an incompatible host and client declaration must not reuse the same Cordis Context key — even when they use independent runtime contexts, TypeScript declaration merging still sees both types.

README contract and verification commands for a new DSH plugin package

README and display metadata

The package README is not optional; it carries the service API, extension points, and the model context contract (source). Order requirements:

  1. Write package-specific content first — service API, config, events, extension points, and design notes come first. Expect: the first screen a consumer sees is the interface.
  2. Pick frontmatter by kind — group, reference, library, or bundle, exactly one, and each kind maps to exactly one README template. Expect: the document structure matches the package's place in the repo.
  3. Record only persistent gaps under limitations — record consumer-visible gaps and non-obvious maintainer constraints owned by this package, and leave routine cleanup in source TODOs. Expect: the document does not turn into a changelog.
  4. Add Model Experience and Known Limitations and Deferred Work — one H3 per model-context entry, with the ordered fields What the model sees, Token effect, and KV Cache effect; packages with no context effect or only consumer-owned paths use the audited None, as or Indirectly, through statements, and generic packages unrelated to the model may declare not applicable. Expect: the validator's required section structure passes.

Display metadata is another optional layer: define meta.title and meta.description in locale/en.json, merge ./package.json and ./locale/*.json into exports, and add locale/*.json to files. The fallback chain is "title: locale meta.title → package.json.name → full Cordis plugin name" and "description: locale meta.description → package.json.description → nothing shown." To show an icon on cards, details, and component rows, set "icon": "./icon.svg" at the top level of the export manifest and add it to files; the image must be self-contained and no larger than 256 KiB, supporting SVG, PNG, JPEG, and WebP, and absolute paths, URLs, and out-of-directory paths are rejected.

Verification commands

A new package is done when the repository-level commands all pass, not when it merely runs locally (source). Run them in this order:

  1. Register and sync metadata — run pnpm install at the repo root to register the workspace, then pnpm run doc-sync to sync docs. Expect: the workspace and docs are consistent.
  2. Run constraints, types, and lint — run pnpm run constraints, pnpm run typecheck, and pnpm run lint in order. Expect: package.json invariants, type resolution, and lint all green.
  3. Run build and hygiene — run pnpm run build and pnpm run hygiene. Expect: build output and hygiene checks pass.
  4. Optionally check display metadata — if the package provides display metadata, run pnpm run verify-package-meta. Expect: display fields, asset exports, and published-file coverage pass.
sh
pnpm install                    # register the workspace
pnpm run doc-sync
pnpm run constraints && pnpm run typecheck && pnpm run lint
pnpm run build && pnpm run hygiene
pnpm run verify-package-meta    # only when the package provides display metadata

Self-check three things after writing: are the directory and five files complete; are the package.json invariants and root tsconfig registration both done; are the README Model Experience and limitations sections filled in. To understand the underlying framework first, read the Cordis primer; a finished plugin can go into DSH Plugin Hub for people to find.

FAQ

When should you add a DSH plugin workspace package, and how is it different from a third-party plugin?

Add a DSH plugin workspace package under packages only when a capability must be reused by several packages in the repo or evolve independently as a replaceable capability. A third-party DSH plugin is installed externally from DSH Plugin Hub, never lands in this repo's packages directory, and is not bound by the package.json invariants here.

Where does a new DSH plugin package live, and what package.json invariants apply?

A new DSH plugin package always lives under packages/<group>/<pkg>/ and is named @deepseek-ai/dsh-<name>. Its package.json is enforced by pnpm run constraints: private is true, version matches the root package.json, type is module, main points to lib/index.js, types points to lib/types/index.d.ts, and @deepseek-ai/cordis appears in both peerDependencies and devDependencies with the same range.

Why must a DSH plugin package be registered in tsconfig, and how do you name directories and ctx keys?

A DSH plugin package must add its own path to the references of tsconfig.host.json or tsconfig.client.json, and an ordinary package belongs to exactly one aggregate; miss this step and project references plus type resolution cannot find the package, so typecheck fails. Names must describe the current stable responsibility: use the capability name for interface packages and a mechanism/protocol/environment/vendor qualifier for implementations, singular ctx keys for engine, runtime, or policy, and plural for a registry.

What should the README and display metadata of a DSH plugin package contain?

A DSH plugin README starts with service API, events, extension points, and design notes, then picks frontmatter by kind, records only persistent gaps under limitations, and fills in the Model Experience fields What the model sees, Token effect, and KV Cache effect. Display metadata defines meta.title and meta.description in locale/en.json, and an icon is set at the top level of the export manifest and added to files.

In what order should you verify a new DSH plugin package after writing it?

A new DSH plugin package is done only when the repository-level commands pass, not when it merely runs locally. At the repo root, run pnpm install, pnpm run doc-sync, pnpm run constraints, pnpm run typecheck, pnpm run lint, pnpm run build, and pnpm run hygiene in order; if the package provides display metadata, also run pnpm run verify-package-meta to check fields and published-file coverage.

Related Terms

workspace package
A workspace package is an independent unit inside the DeepSeek Harness repository, named @deepseek-ai/dsh-<name> and located under packages/<group>/<pkg>; one package can serve as a Service Definition, a Service Provider, or a Consumer at the same time.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/adding-a-package
package.json invariants
package.json invariants are the manifest constraints DeepSeek Harness enforces on every workspace package, covering private, version, type, main, types, exports, and where dependencies appear, all checked together by pnpm run constraints.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/adding-a-package
Service Definition
A Service Definition is the package that declares the interface in a capability seam; together with the Service Provider that implements it and the Consumer that uses it, the three roles form a replaceable capability, and no single role is a seam.— https://deepseek-harness.github.io/deepseek-harness/en/reference/capability-seams
Model Experience
Model Experience is a fixed README section that describes what a package contributes to the model context, with each entry explained by the ordered fields What the model sees, Token effect, and KV Cache effect.— https://deepseek-harness.github.io/deepseek-harness/en/reference/cookbook/adding-a-package

Sources