DSH 插件开发指南

August 14, 2026 · View on GitHub

DSH(DeepSeek Harness)构建在 Cordis v4 之上,插件就是一个标准的 Cordis 插件 npm 包,由 dsh 的 loader 按 profile 的补丁树加载。本文从零说明如何在本仓库新增一个插件、 写一个工具插件、以及把它接入 profile。

1. 插件模块的约定(loader 形状)

loader(@deepseek-ai/cordis-plugin-loaderimport() 插件包后,会把导出对象直接交给 Cordis 的 registry.plugin()。因此插件包主入口必须具名导出

export const name = "my-plugin";        // 插件标识(诊断/日志用)
export const inject = ["tools"];        // 需要的服务,加载器会等这些服务就绪
export const Config = z.object({ ... }); // schemastery 配置 schema(GUI 设置表单也用它)
export function apply(ctx, config) { ... }

要点:

  • 不要 export default。loader 的 unwrapExports 会取 exports.default ?? exports, 一旦有 default,就会丢掉 name/inject/Config/apply 形状。
  • Config@deepseek-ai/schemasteryz(不是 zod)。schema 是可调用函数, 直接 Config(value) 校验,没有 .parse()。类型用 Schemastery.TypeT<typeof Config>
  • inject 里列出本插件必需的服务名;loader 按服务可用性驱动激活顺序。

2. 新建一个插件包

复制 packages/tool-hello 作为模板即可:

packages/<你的插件>/
  package.json      name 用 @neo-dsh/<...>,main/types 指向 lib/,导出 "./src/*"
  tsconfig.json      extends ../../tsconfig.base.json,rootDir=src,outDir=lib
  src/index.ts       Cordis 插件本体
  README.md

package.json 关键字段:

{
  "name": "@neo-dsh/<...>",
  "type": "module",
  "main": "lib/index.js",
  "types": "lib/index.d.ts",
  "exports": { ".": { "types": "./lib/index.d.ts", "default": "./lib/index.js" } },
  "scripts": { "build": "tsc -p tsconfig.json", "typecheck": "tsc -p tsconfig.json --noEmit" }
}
  • 运行期 import 的包放 dependencies(如 @deepseek-ai/dsh-tools@deepseek-ai/schemastery)。
  • 仅类型引用、由宿主进程提供的包放 peerDependencies(如 @deepseek-ai/cordis)。
  • 构建用 tsc 产出 lib/lib 不入库(见根 .gitignore)。

3. 写一个工具插件(Tool Plugin)

工具注册在共享的 ctx.tools 上,用 @deepseek-ai/dsh-toolsdefineTool

import z from "@deepseek-ai/schemastery";
import { defineTool } from "@deepseek-ai/dsh-tools";
import type { Context } from "@deepseek-ai/cordis";

export const name = "my-tool";
export const inject = ["tools"];
export const Config = z.object({ prefix: z.string().default("Hi") });
export type Config = Schemastery.TypeT<typeof Config>;

export function apply(ctx: Context, config: Config): void {
  ctx.tools.register(defineTool({
    name: "my_tool",
    description: "模型可见的描述……",
    parameters: {
      target: { type: "string", required: true, description: "……" },
    },
    output: {
      schema: {
        type: "object",
        additionalProperties: false,
        properties: {
          message: { type: "string", required: true },
        },
      },
      render: (_args, value) => [{ type: "text", text: value.message }],
    },
    async execute(args) {
      return { message: `${config.prefix}, ${args.target}!` };
    },
    isConcurrencySafe: () => true,
    presentCall: (args) => ({
      card: "generic", title: "My tool", kind: "other", rawInput: args,
    }),
  }));
}

要点:

  • parameters 用 dsh-tools 的 ParameterSchemaSpec DSL(type + required: true + description)。
  • output.schema必填的规范输出 schema,render 把规范值投影成模型可见的 ContentBlock[]
  • execute 返回 promise,值必须匹配 output.schema
  • presentCall / presentResult 是纯函数,决定 GUI 卡片如何展示调用/结果。
  • 可选钩子:timeoutMs(配合 dsh-tool-call-timeout-policy)、finalizeContentpresentResult

其它插件类型同理:按你要扩展的服务选择 inject 与要注册的东西(如 ctx.on(...) 事件、 client UI 插件 dsh-client-ui-* 等)。

4. 接入一个 dsh profile

  1. 软链插件包进 profile 的 node_modules(插件依赖自包含解析,无需在 profile 装依赖):

    $prof = "$env:USERPROFILE\.dsh\profiles\web"
    New-Item -ItemType Directory -Path "$prof\node_modules\@neo-dsh" -Force
    New-Item -ItemType Junction -Path "$prof\node_modules\@neo-dsh\<pkg>" -Target "D:\selfs\neo_dsh_plugins\packages\<pkg>"
    
  2. cordis.patch.yml 启用(profile 的用户补丁层,优先级最高):

    - insert:
        - id: my-tool
          name: '@neo-dsh/my-tool'
          config:
            prefix: Hi
    
  3. 验证

    # 组合配置树里有这一行
    dsh --profile web --dump-config | Select-String "my-tool"
    # 从 profile 目录经 junction 能解析到模块
    node scripts/verify-profile-load.mjs   # cwd 设为 profile 目录
    
  4. GUI 实时生效验证(最权威):直接查运行中 GUI 的 Loader 清单 (typert pluginInventory/list RPC),确认插件 enabled: truefiberPhase: "active"

    node scripts/verify-gui-plugin.mjs          # 默认查 @neo-dsh/tool-hello
    node scripts/verify-gui-plugin.mjs "my-tool" # 或按关键字查
    # 输出 GUI-LOADED 即已生效
    
  5. 独立运行期测试(不依赖 GUI,用真实 dsh-tools 注册表跑 apply + 执行工具):

    npx --yes pnpm@10 --filter @neo-dsh/tool-hello test
    
  6. GUI 生效:profile 装了 HMR(cordis-plugin-hmr,root .),改 cordis.patch.yml 会触发热载入。若未热载入,重启 web profile;新会话中模型即可看到新工具。 可用 verify-gui-plugin.mjs 确认加载状态。

5. 常见问题

  • 工具没出现在模型工具集里:确认 inject: ["tools"]、patch 行 name 与包名一致、 插件在 profile node_modules 可解析;然后新开一个会话再试(工具集按请求组装)。
  • Config 校验不生效/表单不显示Config 必须是 schemastery schema,字段加 description 方便 GUI 表单渲染。
  • 不要在生产 profile 上做破坏性实验:先 Copy-Item cordis.patch.yml cordis.patch.yml.bak 备份。