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-loader)import() 插件包后,会把导出对象直接交给
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/schemastery的z(不是 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-tools 的 defineTool:
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 的ParameterSchemaSpecDSL(type+required: true+description)。output.schema是必填的规范输出 schema,render把规范值投影成模型可见的ContentBlock[]。execute返回 promise,值必须匹配output.schema。presentCall/presentResult是纯函数,决定 GUI 卡片如何展示调用/结果。- 可选钩子:
timeoutMs(配合dsh-tool-call-timeout-policy)、finalizeContent、presentResult。
其它插件类型同理:按你要扩展的服务选择 inject 与要注册的东西(如 ctx.on(...) 事件、
client UI 插件 dsh-client-ui-* 等)。
4. 接入一个 dsh profile
-
软链插件包进 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>" -
在
cordis.patch.yml启用(profile 的用户补丁层,优先级最高):- insert: - id: my-tool name: '@neo-dsh/my-tool' config: prefix: Hi -
验证:
# 组合配置树里有这一行 dsh --profile web --dump-config | Select-String "my-tool" # 从 profile 目录经 junction 能解析到模块 node scripts/verify-profile-load.mjs # cwd 设为 profile 目录 -
GUI 实时生效验证(最权威):直接查运行中 GUI 的 Loader 清单 (typert
pluginInventory/listRPC),确认插件enabled: true且fiberPhase: "active":node scripts/verify-gui-plugin.mjs # 默认查 @neo-dsh/tool-hello node scripts/verify-gui-plugin.mjs "my-tool" # 或按关键字查 # 输出 GUI-LOADED 即已生效 -
独立运行期测试(不依赖 GUI,用真实
dsh-tools注册表跑apply+ 执行工具):npx --yes pnpm@10 --filter @neo-dsh/tool-hello test -
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备份。