跳到主要内容

开发指南:DeepSeek Harness Cordis 插件运行时工程

七天实战

插件与 Bundle 实战请配合 7 天学会 DeepSeek Harness Day 2、Day 4、Day 7 及 技能实验室 同步练习。

延伸阅读

架构认知见 入门介绍;安装见 快速开始;生产安全见 最佳实践;开源对照见 GitHub 项目导读

本文定位与读者前提

本文面向已经理解「Agent = Model + Harness」、并在本地成功启动过 Web UI 的工程师。目标不是复述 CLI 命令,而是系统讲授 Cordis 插件运行时 的工程知识:如何写可卸载的插件、如何扩展工具与审批、如何在 Host / Preset / Client 三平面上协作、如何用测试与调试手段把不确定性压到可接受范围。

若你尚未建立 Cordis 五核心概念(插件、服务、Effect、Bundle、事件),建议先阅读 从零到一 Day 1–2,再回来精读本文。本文所有内部链接均使用 /docs/dsh/... 路径,便于在本站侧边栏中跳转。

阶段核心产出常见失败
本地插件可挂载/卸载、Teardown 完整Effect 泄漏、循环依赖
工具扩展Schema 可被模型稳定调用描述歧义、参数漂移
BundleProfile 可复现peerDependencies 未声明
生产可观测、可 Replay绝对路径硬编码

第一章:Cordis 插件工程基础

1.1 Everything is a Plugin 的工程含义

DeepSeek Harness 把模型接入、工具管道、会话管理、Web UI 槽位、审批策略都视为 可组合插件。这意味着:

  1. 能力边界清晰:每个插件只应注册自己领域的服务与副作用。
  2. 生命周期可逆:卸载插件时,系统必须回到「仿佛从未挂载」的状态(在持久化数据允许的范围内)。
  3. 依赖可解析:插件通过 inject 声明依赖,Cordis 按拓扑排序挂载,避免隐式全局单例。

最小插件骨架如下:

import type { Context, Plugin } from "@cordis/core";

export default {
name: "example-hello",
deps: ["@dsh/core-tools"],
apply(ctx: Context) {
ctx.provide("hello", {
greet(name: string) {
return `Hello, ${name}`;
},
});
},
} satisfies Plugin;

1.2 apply(ctx):插件唯一入口

apply(ctx) 是插件与 Cordis 内核的 唯一契约入口。在此函数内,你应当完成:

职责API禁止事项
注册服务ctx.provide(key, impl)在 provide 内启动不可停止的后台任务
声明副作用ctx.effect(fn)直接 setInterval 而不注册 effect
订阅事件ctx.on(event, handler)在 handler 内修改全局变量而不清理
扩展工具注入 tools 服务后注册绕过 sandbox 直接读磁盘
读取配置ctx.config / inject 的配置服务在 apply 顶层 await 长 IO

挂载顺序:Cordis 根据 depsinject 声明构建依赖图;若 A 依赖 B,则 B 的 apply 先于 A 执行。循环依赖会在启动期抛出明确错误——这是特性,不是缺陷。

1.3 ctx.provide 与服务注册

ctx.provide(serviceKey, implementation)当前作用域 注册服务。服务键建议使用命名空间,例如 tools.registryllm.router,避免与核心插件冲突。

interface MyAnalyzer {
analyze(text: string): Promise<AnalysisResult>;
}

export default {
name: "my-analyzer",
apply(ctx) {
const analyzer: MyAnalyzer = {
async analyze(text) {
return { tokens: text.length, lang: "zh" };
},
};
ctx.provide("myAnalyzer", analyzer);
},
};

消费服务 的两种方式:

  1. ctx.inject(key):在 apply 内同步声明依赖,Cordis 保证被依赖服务已 provide。
  2. ctx.get(key):运行时获取;若不存在返回 undefined(用于可选依赖)。
模式适用风险
inject 必需依赖核心路径缺失依赖启动失败(期望行为)
get 可选依赖增强功能需处理 undefined
provide 工厂延迟初始化工厂内 effect 仍需 Teardown

1.4 ctx.inject 与依赖注入语义

inject 不仅是「取服务」,更是 配置期可见的依赖边。在 Bundle 诊断中,inject 边会出现在依赖图里,便于排查「为何某插件在 Minimal 预设未挂载」。

失败模式:

现象根因修复
Service not found: llm预设未包含 llm 插件在 Profile 中加入 @dsh/plugin-llm
挂载顺序错误deps 未声明deps 数组中列出插件名
拿到 stub 实现Mock 插件覆盖检查测试 Bundle 的 patch

1.5 ctx.on() 与事件总线

Cordis 事件用于 横切关注点:会话生命周期、工具执行前后、审批结果、插件热重载等。订阅务必在 effect 内注册并在 Teardown 中 off

apply(ctx) {
ctx.effect(() => {
const handler = (payload: ToolExecuteEvent) => {
ctx.logger.info("tool executed", payload.toolName);
};
ctx.on("tool:after", handler);
return () => {
ctx.off("tool:after", handler);
};
});
}

常用事件域(概念性,以官方类型定义为准):

事件域典型用途
session:*创建、Resume、Fork、压缩
tool:*注册、执行、失败、审批
plugin:*挂载、卸载、错误
llm:*请求、流式 chunk、用量
agent:*Spawn、Fork、子 Agent 结束

反模式:在 on 回调里 await 长任务而不限流,会阻塞事件环;应投递到内部队列或 ctx.effect 管理的 worker。

1.6 ctx.effect() 与可逆副作用

ctx.effect(setup) 是 Cordis 区别于普通 DI 框架的核心。setup 可返回 Teardown 函数;插件卸载或作用域销毁时,Cordis 逆序调用 Teardown。

apply(ctx) {
ctx.effect(() => {
const timer = setInterval(() => refreshCache(), 30_000);
const sub = externalBus.subscribe(onMessage);
return () => {
clearInterval(timer);
sub.unsubscribe();
};
});
}

Effect 栈模型:


第二章:Effect 泄漏与检测

2.1 什么是 Effect 泄漏

Effect 泄漏指 Teardown 未执行或未完整执行,导致定时器仍在触发、文件句柄未关闭、重复挂载时双注册工具或监听器、内存与句柄单调上涨。

2.2 泄漏模式对照表

模式代码气味后果
裸 setInterval不在 effect 中卸载后仍运行
只 off 不清资源Teardown 空函数句柄泄漏
async effect 无取消await 长任务卸载后仍写会话
全局 Map 缓存插件态无 weak ref插件无法 GC
重复 provide热重载未清理后者覆盖前者,行为随机

2.3 检测手段

  1. 插件树 + 挂载计数:开发 Host 开启 debug,反复挂载/卸载同一插件,观察工具数量与监听器计数是否归零。
  2. 依赖图 diff:对比挂载前后 dsh plugin graph 输出。
  3. Session Replay 回归:泄漏常表现为 Replay 时重复执行副作用。
  4. Node 堆快照:对长期运行 Host,强制 GC 后对比 retained size。
  5. 自动化测试:Vitest 中循环 mount → unmount 100 次,断言无 unhandled rejection。

2.4 工程清单:Effect 健康

  • 每个 setInterval / setTimeout 在 Teardown 中 clear
  • 每个 ctx.on 在 Teardown 中 ctx.off
  • 子进程 / Worker 在 Teardown 中 kill 或 terminate
  • async 任务监听 AbortSignal,卸载时 abort
  • 不在模块顶层注册副作用

第三章:绝对路径注册与 peerDependencies

3.1 为何强调绝对路径

Harness 在生产环境可能从不同工作目录启动;Bundle 内插件引用若使用相对路径,会导致找不到插件入口、Schema 加载失败、沙箱 allowlist 不一致。

推荐做法:在 dsh.plugin.json 中使用基于 Bundle 根的路径解析;Host 启动时将 Bundle 根注入 DSH_BUNDLE_ROOT

{
"name": "my-org.analytics",
"entry": "./dist/index.js",
"schemaRoot": "./schemas",
"peerDependencies": {
"@cordis/core": "^1.2.0",
"@dsh/runtime": "^0.8.0"
}
}

3.2 peerDependencies 与 Cordis 版本

插件 不应@cordis/core 打包进 bundle。应:

  1. peerDependencies 声明兼容的 Cordis 主版本。
  2. 在 CI 中用矩阵对多个 @dsh/runtime 版本跑测试。
  3. 在 README 中维护 兼容性矩阵(见 GitHub 项目导读)。
Cordis 变更类型插件应对
服务键重命名提供 adapter 插件
effect 语义收紧补 Teardown 测试
新 inject 必需项更新 deps 声明

第四章:核心服务详解(tools / llm / sessions / sandbox / fs)

Harness 核心插件通过服务暴露能力。自定义插件应 inject 而非复制 这些服务。

4.1 tools 服务

职责:工具注册、Schema 校验、执行管道、与审批/沙箱协作。

const tools = ctx.inject("tools");
tools.register({
name: "read_config",
description: "Read a config key from allowed paths",
parameters: {
type: "object",
properties: {
key: { type: "string", description: "Config key name" },
},
required: ["key"],
},
async execute(args, execCtx) {
return { value: await loadConfig(args.key) };
},
});

4.2 llm 服务

职责:模型路由、流式、Token 计量、多 Provider failover。扩展方式:提供 llm.middleware 插件,在 effect 中注册拦截器,而非 monkey-patch SDK。

4.3 sessions 服务

职责:Append-only Session Log、Resume、Fork、Replay、上下文压缩。

4.4 sandbox 服务

职责:路径 allowlist、命令模板、环境变量过滤、网络 egress 策略。自定义工具 必须 通过 sandbox API 访问文件系统。

4.5 fs 服务

职责:在沙箱约束下的读写、临时目录、原子写入。

操作走 sandbox/fs直接 Node fs
读用户项目内文件
写 Session 附件
读 bundle 内 JSON通过 Bundle 根解析谨慎

第五章:自定义工具 Schema 与描述工程

模型选工具 几乎完全依赖 名称、描述与 JSON Schema。描述工程的目标是提高 首次调用成功率参数合法率

5.1 Schema 设计原则

  1. 字段名与代码一致repoPath 不要在 Schema 写 path
  2. required 最小化:能默认值的放 default。
  3. enum 优于 free text:能枚举的操作类型不要用 string。
  4. description 写清边界:单位、范围、禁止行为。
  5. 示例优于长文:在 README 给 2–3 个调用样例。

5.2 描述 anti-pattern

问题描述模型行为
"处理文件"随机选 read/write/delete
无 sandbox 说明尝试读系统敏感路径
返回结构未文档化幻觉字段

5.3 描述工程清单

  • 工具名动词开头、全局唯一
  • description 含读写属性与安全边界
  • 每个参数有 description
  • 复杂工具提供 negative example
  • 在 Standard 与 Code 预设各测一轮调用

第六章:审批策略扩展

危险工具进入 审批管道。插件可注册 approval policy provider 扩展策略。

apply(ctx) {
const approval = ctx.inject("approval");
approval.registerPolicy({
id: "org-deny-rm-rf",
priority: 100,
match(call) {
return call.toolName === "shell" && /\brm\s+-rf\b/.test(call.args.command);
},
decide() {
return { action: "deny", reason: "rm -rf blocked by org policy" };
},
});
}
策略类型典型 priority
硬 deny90–100
需人工50–70
审计仅记录10–20

失败模式:策略过宽导致 审批疲劳;过严导致 任务无法完成。应对:按预设分级,Code 预设放宽只读工具,Creator 预设收紧网络。


第七章:Bundle 打包与 dsh plugin CLI

7.1 Bundle / Profile / Patch 三层

职责版本化
Bundle插件集合与默认配置
Profile环境(dev/staging/prod)
Patch局部覆盖单个插件配置可选

7.2 dsh plugin CLI 常用命令

dsh plugin init my-plugin --template typescript
dsh plugin link .
dsh plugin build
dsh plugin validate
dsh plugin graph --bundle ./bundle.yaml
dsh plugin pack --out ./dist/my-plugin-1.0.0.tgz

7.3 打包注意事项

  1. external cordis:标记 @cordis/core@dsh/runtime 为 external。
  2. 复制 schemas:构建脚本将 schemas/ 复制到 dist/
  3. 双 target:部分插件需 Node + 浏览器 Client 分包。

第八章:三平面开发(Host / Preset / Client slots)

平面运行环境插件示例
HostNode 服务端会话存储、MCP 客户端
PresetHost 内逻辑配置工具包、模型路由
Client浏览器 / TUI面板、可视化、快捷键

Client 插件 通过 clientSlots 注册 UI 组件,不得 直接访问 Node API;通过 RPC 与 Host 通信。

开发工作流:先在 Minimal 验证 Host 插件 → Standard 测工具协同 → Client dev server 联调槽位 → Creator 仅在隔离环境验收。


第九章:子 Agent Spawn / Fork 编程模式

操作Session 关系典型用途
Fork分支,共享前缀历史尝试 alternative 方案
Spawn新 Session,可选继承上下文子任务委派、并行
const agents = ctx.inject("agents");
const child = await agents.spawn({
preset: "minimal",
tools: ["read_file", "grep"],
task: "Summarize ./docs only",
timeoutMs: 120_000,
});
const result = await child.wait();

编程约束:

  1. 工具子集:子 Agent 工具集必须是父集子集或经审批的扩展。
  2. 预算:限制 Token、步数、wall time。
  3. 结果汇总:Spawn 返回 structured result,避免子 Session 全文灌回父上下文。
  4. 取消传播:父 Session 取消时 abort 子 Agent。

Map-Reduce Spawn:父 Agent 拆 N 个子任务并行 Spawn,子 Agent 仅返回 JSON 摘要,父 Agent reduce 合并。失败常见于子 Agent 返回全文而非摘要——应在 Spawn 参数中指定 outputSchema

失败模式:子 Agent 死循环(未设 maxSteps)、上下文爆炸(日志未压缩)、权限提升(工具集大于父策略允许)。


第十章:测试策略

10.1 最小插件集(Test Bundle)

维护专用 bundle.test.yaml@dsh/plugin-core + 被测插件;llm@dsh/plugin-llm-mocksandbox 指向临时目录;禁用网络 egress。

10.2 Mock 服务

export const mockLlmPlugin = {
name: "llm-mock",
apply(ctx) {
ctx.provide("llm", {
async complete() {
return { content: "mock response", usage: { totalTokens: 10 } };
},
});
},
};

10.3 测试金字塔

层级内容
单元Schema 校验、policy match
集成挂载插件调用 tools.execute
端到端Playwright 驱动 Web UI + Replay

10.4 测试清单

  • mount/unmount 无泄漏
  • 工具 golden snapshot
  • 审批 deny/allow 用例
  • Spawn 超时与取消
  • peerDependencies 矩阵 CI

第十一章:调试手段

11.1 插件树

dsh host debug plugins --tree

输出挂载顺序、版本、deps 满足情况。

11.2 依赖图

dsh plugin graph --format mermaid

用于 PR Review:新增 inject 是否引入环。

11.3 Session Replay

dsh session replay --id sess_abc --speed 2 --breakpoint tool:after

用于复现「模型为何连调三次失败工具」类问题。

11.4 日志级别

变量用途
DSH_LOG=cordis:*插件生命周期
DSH_LOG=tools:*工具管道
DSH_LOG=session:*事件溯源

第十二章:TypeScript 项目结构示例

my-dsh-plugin/
├── package.json
├── dsh.plugin.json
├── tsconfig.json
├── src/
│ ├── index.ts
│ ├── services/analyzer.ts
│ ├── tools/register.ts
│ ├── tools/schemas/analyze.json
│ ├── policies/approval.ts
│ └── client/panel.tsx
├── tests/mount.test.ts
└── scripts/copy-schemas.mjs

package.json 片段:

{
"name": "@my-org/dsh-plugin-analytics",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"peerDependencies": {
"@cordis/core": "^1.2.0",
"@dsh/runtime": "^0.8.0"
},
"scripts": {
"build": "tsup src/index.ts --format cjs --dts --external @cordis/core",
"test": "vitest run",
"validate": "dsh plugin validate"
}
}

第十三章:综合失败模式与排障表

症状可能原因第一步
插件不显示Profile 未包含检查 bundle.yaml
工具重复热重载泄漏查 effect Teardown
审批卡死策略 priority 冲突导出 approval 决策链
Replay 不一致非确定性工具mock 或 record 固定 seed
Client 槽位空白RPC 未连查 Host CORS / WS

第十四章:开发工程总清单

插件作者发布前

  • apply 内无裸副作用
  • peerDependencies 已声明 Cordis / runtime 版本
  • 工具 Schema 与描述经 Standard 预设实测
  • README 含兼容性矩阵与最小 Host 版本
  • dsh plugin validate 通过
  • mount/unmount 测试 50 次以上无泄漏
  • 审批策略与 org 安全基线对齐
  • Client 插件不泄露 Node API

Code Review 关注点

  • inject 依赖是否必要且最小
  • 子 Agent 工具集是否最小权限
  • 日志是否含敏感信息
  • Bundle patch 是否可逆

附录 A:apply 生命周期深度剖析

当 Host 启动时,Cordis 内核首先解析 Bundle 清单,将所有插件标识符展开为具体模块路径。若你使用绝对路径注册,Host 会在解析阶段将 $DSH_BUNDLE_ROOT 代入,确保无论从哪个工作目录执行 CLI,入口文件均可定位。解析完成后,内核构建有向无环图;若检测到环,启动立即失败并打印环上插件名——这比运行时随机缺服务更易调试。

挂载阶段按拓扑序调用每个插件的 apply。在单个 apply 执行过程中,禁止 await 网络 IO 或磁盘大读取;若必须加载配置,应使用同步可读的小文件,或注册 effect 在挂载完成后异步加载。许多新手在 apply 顶层 await fetch() 拉远程配置,导致 Host 启动时间不可控,且卸载时 fetch 无法取消——这是典型的 Effect 边界错误。

provide 发生在 apply 同步执行期间。若两个插件 provide 同一键,后挂载者覆盖前者(除非内核启用了 protected 键保护)。因此官方插件应使用 @dsh/ 前缀,社区插件使用组织前缀,避免意外覆盖 toolsllm 核心服务。覆盖本身不是 bug,而是显式 Patch 机制的用途;无意覆盖才是 bug。

effect 注册可以嵌套:在 effect 的 setup 中再注册 effect,Teardown 顺序为 后注册先销毁,类似栈。利用这一特性,可以在外层 effect 分配共享资源,在内层 effect 注册多个监听器,外层 Teardown 统一释放资源。反之,若在内层 effect 打开文件,外层 Teardown 才关闭,内层必须先关闭——否则顺序错误导致泄漏。

ctx.on 与 Node EventEmitter 类似,但增加了 作用域隔离:子作用域插件卸载时,只移除该作用域注册的 listener,不影响兄弟插件。跨作用域广播使用全局 bus 时要格外谨慎,优先使用命名事件如 org:audit:tool 而非泛化 message

理解生命周期后,阅读官方 plugin-host 源码时应对照:何处创建 root ctx、何处为 Preset 创建 child ctx、Client 槽位插件是否运行在单独 bundle。三平面并非三个进程,而是 三个配置维度;同一进程内可通过不同 Profile 切换 Preset,而 Client 插件可能由 Vite 单独打包。

调试生命周期问题时,开启 DSH_LOG=cordis:lifecycle 可看到 mount/unmount 成对日志。若只有 mount 无 unmount,说明 Host 未走正常 shutdown(kill -9)或插件抛错中断了 Teardown 链——此时应修复抛错插件,而非责怪内核。Host 升级或 Profile 切换触发的热重载路径与进程退出路径不同;插件作者应在两种路径下各测一次 unmount,避免「正常退出无泄漏、热重载泄漏」的隐蔽 bug。若 Teardown 中抛错,Cordis 会记录但继续销毁后续 effect——因此 Teardown 本身也应 try/finally,确保关键资源释放。


附录 B:工具执行管道与 sandbox 协作细节

工具从注册到执行经历:Schema 入库 → 模型可见性过滤 → 调用解析 → 审批链 → sandbox 包装 → 实际 execute → 结果审计 → 写入 Session。自定义插件通常在注册阶段注入 Schema,在 execute 阶段访问 inject 的 fs 服务而非原生 fs 模块。

sandbox.fs.read(path) 会先规范化路径,检查是否落在 allowlist 内。allowlist 由 Preset 与 Host 环境变量共同决定;Creator 预设可能扩大可写范围,但应在 README 与审批策略中明确警示。插件作者不应尝试「绕过」sandbox,而应在工具设计阶段就假设 所有 IO 均被拦截

审批链按 priority 降序匹配;首个返回 deny 的策略终止管道。若所有策略 pass,且工具标记为 requiresApproval: true,则进入人工或 UI 审批队列。插件扩展审批时,应使用明确 id,便于 Session Replay 显示「被哪条策略拦截」。测试审批插件时,准备三条用例:deny、ask、allow。

工具返回值的 JSON 可序列化性由内核校验;返回 BigInt、循环引用对象会导致 Session 写入失败。工程上应返回 plain object,大文件返回 句柄或路径引用 而非内联 base64(除非明确 size limit)。

描述工程与执行管道交叉:若 Schema 声明 maxLength 而 execute 未校验,模型可能传入超大字符串导致 OOM——Schema 与 execute 必须 双重校验。同理,enum 字段在 execute 内应 assert,防止直接 API 调用绕过模型。

MCP 工具与原生工具在管道中会合并为统一命名空间;注意避免工具名冲突。社区实践是在 MCP 工具名加前缀 mcp_<server>_<tool>。读官方 mcp-client 插件源码可见映射逻辑,是学习的优秀范本。

长会话中工具列表可能因上下文压缩而被裁剪;描述工程应让 工具名自解释,避免过度依赖「见工具 A 的说明」式交叉引用。在 最佳实践 中推荐的「小步发布」同样适用于工具:先发布只读工具,稳定后再发布写工具。工具 execute 内的异常应映射为结构化错误 { code, message, retryable },便于模型决定是否重试;裸 throw Error 字符串会导致 Replay 与 OTel span 难以聚合统计。


附录 C:Spawn 与 Fork 的 Session 数据模型

Fork 在 Session Log 上创建 分支点:新 Session 继承 fork 点之前的事件,之后独立追加。Replay 时选择分支可对比「若选 B 方案」的差异。Spawn 创建 新 Session 树节点,可选择是否继承压缩后的上下文摘要;inherit 过多会导致子 Agent 重复父 Agent 已探索的错误路径,inherit 过少则子 Agent 缺乏任务背景。

编程模式 Map-Reduce Spawn:父 Agent 将大任务拆为 N 个子任务,并行 Spawn N 个子 Session(受 concurrency limit 约束),每个子 Session 仅返回 structured summary,父 Agent 再 reduce 合并。此模式失败常见于子 Agent 返回全文 README 而非摘要——应在 Spawn 参数中指定 outputSchema

编程模式 Speculative Fork:对同一问题 Fork 两路,不同模型或不同工具集,比较 Session 结尾哪路先达成 acceptance criteria。Harness 预设对比实验室即基于此思想。生产环境慎用双 Fork 烧双倍 Token,除非任务价值覆盖成本。

取消语义:父 Session 收到用户 cancel 时,应向所有 active child 发送 abort signal。子插件若在 execute 中忽略 signal,会导致僵尸子 Session——测试应用 fake timer 验证 abort 是否在 5s 内生效。

子 Agent 权限遵循 最小工具集 原则:不是「子 Agent 不能用写工具」,而是「只有需要写的那步子 Agent 才开放写工具」。Session Replay 调试 Spawn 问题时,在 breakpoint 设于 agent:spawnagent:complete,观察子 Session id 与父 id 关联字段。GitHub Issue 报告 Spawn bug 时应附 父子 Session id(可脱敏),否则维护者无法复现。

Fork 与 Spawn 在 Session 存储上的差异会影响 合规审计:Fork 保留完整共同前缀,适合「同一任务尝试不同方案」;Spawn 更适合「委派独立子任务」且可配置是否让父 Session 可见子 Session 全文。企业部署常要求 Spawn 默认 inheritContext: summary-only,并在审批插件中 deny 子 Agent 申请 shell 除非父 Session 已获 elevated 审批。多租户 Host 应为每个租户配置 spawn 并发上限,防止单一 Session fork 出数百子 Agent 耗尽 CPU。


附录 D:dsh plugin CLI 与 CI 集成

本地开发循环:dsh plugin link 将当前插件 symlink 到 dev Host 的 plugins 目录,改代码后 Host 热重载(若启用)。生产禁用 link,仅安装 pack 产物。CI 应跑 dsh plugin validate 与单元测试;发布前跑 dsh plugin pack 并上传 artifact。

GitHub Actions 矩阵示例维度:runtime@0.7runtime@0.8 × node@20node@22。任一格失败则阻塞发布。插件包不应在 CI 内启动完整 Web UI——太慢;用 createTestHost 轻量挂载即可。

validate 命令检查:manifest 字段完整、Schema JSON 合法、peerDependencies 满足、entry 文件存在。常见 CI 失败是 忘记 build 就 validate——在 workflow 中先 pnpm builddsh plugin validate

Bundle 仓库与插件仓库分离时,版本号遵循 semver:breaking 变更 increment major,仅新增工具 increment minor,文档 fix increment patch。在 CHANGELOG 中标注 Which preset affected,便于用户评估升级风险。

打包体积控制:tree-shake 未使用代码;不要把 devDependencies 打进 dist。若插件含 wasm 或 native addon,README 必须声明 支持的平台,并在 CI 交叉编译。

与 Docker 部署结合时,Dockerfile 多阶段:builder 阶段 pack 插件,runtime 阶段仅复制 tgz 并 dsh plugin add。避免在 runtime 镜像内 git clone 社区插件——供应链不可控。Monorepo 内多个插件包时,用 changeset 管理发包;根目录 dsh plugin graph 可可视化包间 deps,防止内部环依赖。


附录 E:TypeScript 类型安全与 inject 模式

为 inject 的服务键定义 模块 augmentation,避免字符串拼写错误:

declare module "@cordis/core" {
interface Services {
myAnalyzer: import("./services/analyzer").MyAnalyzer;
}
}

插件入口使用 satisfies Plugin 确保 apply 签名正确。工具 execute 的参数类型从 Schema 生成(json-schema-to-typescript),减少运行时校验与类型不一致。

测试中使用 createTestHost 时传入 typed plugins array,IDE 可自动补全 inject 键。Mock 服务实现只需 satisfy 接口的子集若测试不涉及;但集成测试应使用完整 mock 行为,避免「测试全绿、生产全挂」。

严格 TS 配置:strict: truenoUncheckedIndexedAccess: true。Effect 回调中捕获错误应 log 并 rethrow 或转换为插件 error 事件,不可空 catch——否则 Teardown 不会运行。

跨包共享类型时,发布 @my-org/dsh-plugin-types 小 package,仅含类型与 Schema JSON,无 runtime 副作用。消费者 peer 此 types 包即可。读 deepseek-harness 源码时,从 types 包开始读比从 CLI 入口读更清晰;对照 GitHub 项目导读 的 packages 阅读笔记模板逐包做笔记。


延伸阅读

完成本文实践后,你应能独立设计可卸载、可测试、可发布的 Cordis 插件,并在三平面上与 Host 团队协作集成。

附录 F:apply 与 ctx 组合模式实战

Cordis 插件开发中,最常见的高级模式是 「provide + effect + on」三位一体:在 apply 开头 provide 服务接口,在 effect 内注册 on 监听并在 Teardown 清理,execute 路径只读 inject 的服务。这种模式把「注册期」与「运行期」严格分离,是大型插件可维护性的基础。反例是在 provide 的对象方法里临时 ctx.on——卸载时无法追踪 listener 归属,必然泄漏。

模式一:装饰器式中间件。LLM 中间件插件不替换 llm 服务,而是 provide llm.middlewareRegistry,在 effect 中向 registry 注册 before/after hook。Teardown 时 unregister。这样多个中间件插件可并存,priority 决定顺序。测试时只挂载被测中间件 + mock llm,断言 hook 被调用次数。

模式二:工具域插件。一个业务域(如「数据库迁移」)对应一个插件,内部再分子模块 registerTools、registerPolicies、registerClientSlot。apply 仅调用三个 register 函数,保持 index.ts 可读。每个 register 函数内部自行 effect,避免巨型 apply。

模式三:可选能力探测。通过 ctx.get("mcp") 判断 Host 是否挂载 MCP 客户端,若存在则注册 MCP 桥接工具,否则跳过。deps 不强制 mcp 插件,但在 README 说明「完整功能需 Profile 含 @dsh/plugin-mcp」。集成测试应覆盖两种 Bundle。

模式四:配置驱动工具集。从 Profile patch 读取 enabledTools: string[],apply 末尾过滤已注册工具。Teardown 无需特殊处理,因工具随插件卸载整体移除。注意:过滤必须在 register 之后、Host 广播工具列表之前完成,否则模型可见未启用工具。

模式五:子作用域插件。高级场景下插件在 apply 内创建 child ctx 并挂载子插件数组——用于「插件包」概念。子 ctx 销毁时子插件 Teardown 自动执行。阅读官方 Bundle 加载源码理解 child ctx 边界;自定义子作用域不当会导致服务键冲突。

组合模式使用的失败信号:启动时间 >5s(apply 内 IO)、卸载后工具数不为零、Replay 事件顺序与 live 不一致。遇到时在插件树中定位最后挂载的插件,二分法 disable 插件定位肇事者。

附录 G:Host 平面与 Preset 平面协作

Host 平面负责 进程级 资源:HTTP/WebSocket 服务、Session 存储路径、OTel exporter、MCP 出站网络策略。Preset 平面负责 能力级 配置:Standard 含完整工具与 Code 向模型,Minimal 仅只读工具,Creator 放宽沙箱但应配审批。插件作者必须声明 适用 Preset,避免在 Minimal 文档中承诺需要 shell 的工具。

Host 插件典型职责:挂载 @dsh/plugin-sessions-store 指定 Session 目录;配置 @dsh/plugin-otel 导出到企业 collector;注册健康检查端点。Preset 插件典型职责:选择模型路由表、工具白名单、默认 approval 策略。同一插件 rarely 横跨两平面;若兼则拆为两个 package 更清晰。

Profile yaml 示例结构(概念性):

bundle: standard@1.2.0
profile: prod-cn
patches:
- plugin: "@my-org/analytics"
config:
enabledTools: ["analyze_log"]
- plugin: "@dsh/plugin-sandbox"
config:
allowPaths: ["/workspace", "/tmp/dsh"]

Client 平面插件不参与 Preset 工具注册,但可能 影响审批 UX(如 Client 插件渲染审批弹窗)。Client 与 Host 的 RPC 契约版本化:Client 发送 approval:request v2 时 Host 需 backward compatible v1。

三平面联调顺序:Host 单插件 smoke → 加 Preset 跑 Session → 开 Client dev 看槽位。跳过 Host 直接在 Client 调 mock API 会导致上线时 RPC 字段不一致。

生产 Host 多实例时,Session 存储插件必须支持 共享存储或 sticky session;仅 Host 平面配置相关,Preset 无感知。插件若缓存 Session 状态在内存,多实例下 Replay 会不一致——应 inject sessions 服务而非自管 Map。

附录 H:Client 槽位与 RPC 工程

Client 槽位(slots)是 Web UI 可插拔区域:侧边栏面板、消息气泡附件、工具执行进度条、设置页表单。注册时声明 slotId、React 组件、所需 Host 权限。Client bundle 由 Vite 构建,不得 import Node 模块;环境变量通过 Host 注入的 window.__DSH_CONFIG__ 读取。

RPC 调用模式:Client host.call('tools.list') 或 typed SDK @dsh/client-sdk。所有 call 应带 sessionId 与 requestId 便于 OTel 关联。错误处理:网络断开显示 offline 态,而非空白屏。

Client 插件测试:Storybook 隔离组件 + MSW mock RPC;E2E 用 Playwright 连真实 dev Host。常见失败:CORS 阻断 WS、slotId 与 Host 注册不一致、组件 SSR 假设 window 存在。

Client 与 Preset 关系:Minimal Preset 可能仍加载 Client 核心壳;自定义 Client 插件可在 Standard+ 才启用。manifest 字段 clientSlots 数组描述 slot 与组件路径。

国际化:Client 插件 UI 字符串应走 i18n 插件提供的 t(),硬编码中文/英文会降低社区插件可复用性。本站中文文档面向读者,插件 UI 仍建议英文 key + 翻译文件。

性能:Client 槽位组件不应在 render 中发起 RPC;用 SWR/React Query 缓存。大列表虚拟滚动,避免 Session 消息上千条时卡顿。

附录 I:工具 Schema 进阶与多模态参数

除基础 JSON Schema 外,DSH 工具描述可扩展 x-dsh 字段(以官方规范为准):x-dsh.readOnlyx-dsh.requiresApprovalx-dsh.timeoutMsx-dsh.idempotent。这些字段影响审批与重试,应在 validate 阶段校验。

数组参数:模型易生成过长数组。用 maxItems 限制,execute 内再次截断并 warn。对象嵌套:深度超过 3 层时模型填参错误率上升——扁平化参数或拆多个工具。

多模态(若 Host 支持 image part):Schema 声明 imageUrlattachmentId,description 说明图片来源必须是 Session 已上传附件,禁止任意 URL 防 SSRF。

工具版本化:同名工具 major 变更时 rename 为 grep_v2 或插件 major bump,Session Replay 旧会话仍映射旧 Schema。deprecation:旧工具 description 首行写 DEPRECATED: use xxx

描述 A/B:可在实验 Profile 用 patch 替换 description 文案,对比首次调用成功率。指标写入 OTel:tool.first_call.success。描述工程 thus 可量化,而非纯玄学。

与 OpenAPI 互导:若工具后端已有 OpenAPI,可用脚本生成 JSON Schema;但需人工补 x-dsh 安全字段。反向:DSH 工具 Schema 导出供文档站生成参考页,保持 SSOT 在插件 repo。

附录 J:审批策略组合与组织基线

企业常在 Host 层预装 org-baseline 插件,registerPolicy 硬 deny 危险命令、强制 shell 走人工审批、记录 audit log 到 SIEM。业务插件不应覆盖 baseline 的 deny,仅可 add 更细规则。

策略调试命令(概念性):dsh approval explain --callId <id> 打印 match 链与最终 decide。Session Replay 在 tool:before 断点可看到 pending approval 状态。

人工审批 UX:Client 插件展示 diff(如将要写入的文件 patch);Host 等待 approval:resolve 事件。超时默认 deny。插件扩展 approval 时可注册 approval renderer 供 Client 展示自定义表单。

Creator 预设:baseline 应更严,deny 出站网络除 allowlist。测试 Creator 插件时在 VM 快照环境进行,避免 Host 机器被改。

合规:审批记录写入 Session Log,保留策略 id 与审批人 identity。GDPR 场景下日志脱敏。插件不得把审批结果写到 Host 外不可审计存储。

与 MCP 工具审批:MCP 工具默认 inherit Host 策略;高 risk MCP server 可在 mcp-client 插件 config 标记 defaultRequiresApproval: true

附录 K:Session 压缩与长会话插件交互

sessions 服务在 Token 接近上限时触发 上下文压缩:摘要旧消息、裁剪工具列表可见性、保留关键 system 与最近 N 轮。插件可通过 session:compress:before 事件注入「不可压缩」标记(如关键合规指令)。

工具插件注意:压缩后模型可能「忘记」早期工具输出,execute 应 idempotent,重复调用不产生破坏。写工具更需 confirmation 或 read-back verify。

长会话调试:Replay 时关闭压缩可还原完整历史;对比压缩开/关行为差异。插件若缓存「会话内状态」,应在 compress 事件清空或 rehydrate。

Fork 与压缩:Fork 点之后压缩独立;Spawn 子 Session 继承摘要时需检查摘要是否含敏感信息,子 Agent 工具集更窄时可 strip secrets。

性能:压缩插件本身不应 O(n²) 扫描全文;应用增量摘要。失败模式:压缩丢关键约束导致模型越权——测用例应含「压缩后仍拒绝非法工具调用」。

附录 L:OpenTelemetry 与插件可观测性

Host 通常挂载 OTel 插件导出 trace/metrics/logs。插件应在工具 execute、Spawn、审批 decide 路径创建 span,属性含 plugin.nametool.namesession.id(哈希)、preset

Metrics 建议:dsh_tool_executions_total{status}dsh_plugin_mount_duration_msdsh_effect_teardown_errors_total。告警 on teardown errors > 0。

日志:使用 ctx.logger 而非 console.log,便于统一格式与级别。禁止 log API key、用户 PII、完整文件内容。

Replay 与 trace:Replay 应生成新 trace id,linked 到原 session id 便于对比。调试「生产失败、Replay 成功」时查 non-deterministic 工具或时间依赖。

采样:高 QPS Host 可 tail sampling;但 tool 失败应 always record。插件自定义 metric 注册 prefix @my-org/ 避免冲突。

Dashboard:Grafana 面板按插件/version 分组,升级插件时观察 error rate 变化。见 最佳实践 运维节。

附录 M:MCP 客户端插件与工具桥接

MCP 将外部 tool server 映射进 DSH 工具 namespace。插件开发者常写 MCP server(Python/TS)而非改 Host。DSH 侧读 mcp-client 配置连接 server,自动 discover tools。

Bridge 注意:MCP tool description 可能简陋——可在 Host patch 用 tool description override 插件增强。SSRF:MCP server URL 必须 allowlist。

生命周期:MCP 连接在 effect 中建立,Teardown close。重连策略 exponential backoff,避免 thundering herd。

测试:用官方 MCP inspector 或 mock server 返回固定 tool list。集成测试不依赖外网 npm MCP 包。

安全:MCP server 等价 RCE 面;Creator 预设 disable 任意 MCP URL。企业用 sideload 签名的 server bundle。

与自定义工具共存:命名冲突时 Host 配置 precedence:native > org > mcp。文档说明优先级避免用户困惑。

附录 N:生产部署与插件版本列车

生产 Bundle 应 pin 精确版本:@dsh/runtime@0.8.3 而非 ^0.8.0。插件同样 pin。升级流程:staging 全量 Replay 回归 → canary Host 10% 流量 → 全量。

回滚:保留上一版 tgz 与 bundle yaml git tag。Session 格式 backward incompatible 时禁止 skip major。

配置密钥:API key 走 Host 环境变量 inject 到 llm 插件,不写进 Profile git。插件 read config 时 redact log。

资源 limit:K8s CPU/memory limit 防止 runaway Spawn。插件 execute 应 respect deadline,AbortSignal 传递。

多区域:各区域独立 Bundle mirror,MCP egress 不同 allowlist。兼容性矩阵注明 region 差异。

变更窗口:内核 major 升级需维护者公告;插件作者订阅 GitHub Release RSS。七天学会子站 community 常同步 breaking 摘要。

附录 O:插件安全审查自查表

发布前安全自查(可打印):

检查
网络默认无任意 egress;文档列出域名
文件仅 sandbox allowlist;无 path traversal
命令shell 工具是否必需;参数是否模板化
密钥无 hardcode;日志 redact
依赖npm audit;无 postinstall 脚本作恶
审批写/删/网络工具默认 requiresApproval
Teardown50 次 reload 无 handle 泄漏
子 Agentspawn 工具集 ⊆ 父策略
供应链pack 可复现 build;checksum 发布
文档README 兼容性矩阵与 threat model 一段

第三方库最小化:Agent 运行时插件不是通用 npm 库,每多一个依赖就多 supply chain 面。vendoring 谨慎,优先官方 SDK。

漏洞响应:plugins 应 SECURITY.md 邮箱;Critical 24h 内发 patch version。用户 pin 版本收 Dependabot。

Red team:用 Creator 预设尝试逃逸 sandbox;用恶意 prompt 诱导工具组合攻击。记录于 docs/threat-tests.md

教育:见 GitHub 项目导读 社区插件五问,安装前必做。

附录 P:常见面试级问题与参考答案

问:为何 Cordis 用 effect 而不是 React 式 hooks? 答:插件生命周期与 UI 渲染无关;effect 强调 mount/unmount 对称与 LIFO Teardown,适合 IO 与订阅。Hooks 的 rules 在插件热重载场景难以保证。

问:provide 与 inject 循环依赖如何处理? 答:设计第三插件或 lazy getter;Cordis 启动期拒绝环。重构为事件驱动或 split interface。

问:工具 execute 是否应抛错? 答:可预期用户错误返回 { error: { code, message } };程序 bug 抛错由 Host 记录并可选中断 Session。

问:Minimal 预设为何还有 llm mock? 答:集成测试与 CI 无 API key;Minimal 面向只读自动化而非对话质量。

问:Client 插件能否调用 llm? 答:不应直连;应经 Host RPC 走统一 metering 与审批。

问:Replay 是否重新执行工具? 答:默认 deterministic replay 可配置 re-execute 用于调试;生产 audit 通常 no-reexecute。

问:Bundle patch 与 env var 优先级? 答:以官方文档为准;一般 env override patch override bundle default。插件 config merge 应可单元测试。

问:多 Preset 如何共享插件代码? 答:单 npm 包多 entry 或 runtime config 切换;避免 copy-paste 两个插件。

问:如何版本化 Session Log schema? 答:内核责任;插件写入 custom event 应 namespaced 且 version 字段。

问:Creator 与 Standard 工具差异谁定义? 答:Preset bundle 清单 + sandbox patch;非单个工具私自放宽。

附录 Q:从 LangChain 迁移的心智映射

LangChain 概念DSH / Cordis 对应
ChainPreset + 工具序列(模型驱动非硬编码链)
Tooltools.register
AgentExecutorHost Agent 循环插件
Memorysessions + 压缩
Callbackctx.on / llm middleware
Runnable configProfile patch

迁移步骤:1)列出 LangChain tools 及 Schema;2)各写 DSH 插件 register;3)用 Session Replay 对比同 prompt 行为;4)审批与 sandbox 补全 LangChain 没有的层。

勿期望一行 API 等价;DSH 强在运行时插件化。LangChain 编排图可保留在 Host 外,DSH 作执行与审计层。

Hybrid:DSH Host 调 LangChain 仅作 RAG 检索子模块——边界清晰,避免双 Agent 循环打架。

附录 R:调试案例库( anonymized )

案例 1:卸载插件后定时器仍打日志。 根因:setInterval 写在 apply 而非 effect。修复:移入 effect Teardown。检测:dsh host debug plugins 看重载次数与 open handles。

案例 2:Standard 可用 Minimal 报 Service llm not found。 根因:插件 inject llm 但 Minimal bundle 无 llm 插件。修复:deps 改 optional 或文档声明仅 Standard+。

案例 3:模型三次调用 grep 均失败。 根因:Schema description 写「正则」未说明 RE2 不支持 lookahead。修复:补 description 与 negative example。验证:Replay 对比改描述前后。

案例 4:Spawn 子 Agent 读到了父 Session 密钥。 根因:inheritContext: full。修复:改 summary-only 并 redact 工具输出。合规:发 security patch minor。

案例 5:Client 槽位白屏仅 prod。 根因:RPC URL 用 localhost,prod Host 不同源。修复:Client 读 DSH_CONFIG.apiBase。

案例 6:CI validate 通过 staging 失败。 根因:peerDependencies 声明 ^1.0 但 staging 仍 0.9。修复:矩阵 CI 与 README 矩阵同步。

案例 7:MCP 工具与 native grep 同名。 根因:未 prefix。修复:mcp_fs_grep;patch precedence。

案例 8:审批 always ask 用户疲劳。 根因:org 策略 priority 10 的 audit 与 50 ask 叠加。修复:只读工具 explicit allow auto。

每个案例对应 FAQ 可链入;贡献者可提交 PR 增案例。

附录 S:dsh plugin init 脚手架字段说明

dsh plugin init 生成:package.jsondsh.plugin.jsontsconfig.jsonsrc/index.tstests/smoke.test.ts.github/workflows/ci.yml。关键字段:

  • name:npm 包名,建议 scope @org/dsh-plugin-*
  • displayName:UI 显示
  • category:tools | integration | ui | policy
  • presets:支持的 Preset 列表
  • permissions:声明 network/fs/shell/spawn 供市场展示

脚手架默认含 example effect 与 example tool,Teardown 正确,可直接改。init 后跑 pnpm testdsh plugin link 验证 dev Host。

模板变体:--template policy 仅审批扩展;--template client-ui 含 client 目录与 vite config。 monorepo 内 init 用 pnpm dsh plugin init -w

升级脚手架:官方 CLI major 可能改 manifest schema;插件跑 dsh plugin migrate(若有)更新 json。旧 schema validate 失败时读 Release note。

本地 symlink link 与 npm pack 差异:link 适合开发,pack 模拟发布;发布前必 pack 安装到干净 Host 测一次。

附录 T:工程度量与完成定义(Definition of Done)

插件 PR 的 DoD:1)CJK 用户文档或英文 README 含矩阵;2)validate + test CI 绿;3)至少一个 Session Replay fixture;4)Teardown 测试;5)Standard preset 手测记录(Issue 或 PR 描述);6)无 @ts-ignore 新增;7)OTel span 若新增 execute 路径则含 plugin.name。

Host 集成 DoD:staging 24h 无 teardown error metric;canary 工具失败率不升;Session 存储增长线性可接受。

文档 DoD:本站 /docs/dsh 内部链接不 404;与 从零到一 Day 对应实验可复现。

团队 DoD:On-call 能根据 Session id Replay;Runbook 含回滚 bundle tag 命令。

完成以上,插件才可称 production-grade,而非仅「能跑 demo」。DeepSeek Harness 生态质量由每个插件 DoD 共同定义。

附录 U:插件开发工作坊分步实验(与七天学会对齐)

实验 1(Day 2):创建 hello-plugin,provide hello.greet,写 Vitest 断言 mount 后 inject 成功。卸载 Host 进程,检查日志 mount/unmount 成对。记录 commit SHA 到实验笔记。

实验 2(Day 2):在 hello-plugin 增加 effect:每 10s 写 debug 日志。故意不写 Teardown,执行 5 次 reload,观察日志是否 5 倍频率——直观理解泄漏。补 Teardown 后再测归零。

实验 3(Day 4):注册工具 echo_upper,Schema 仅 text:string。用 Standard 预设对话「把 hello 转大写」。若模型不调工具,改 description 加入「必须调用工具完成转换」再试——体验描述工程。

实验 4(Day 4):注册 policy deny echo_upper 当 text 含 password。Replay Session 验证 deny 事件写入。尝试 priority 50 ask 与 100 deny 组合。

实验 5(Day 5):对同一 Session Fork 两路,一路允许工具 A,一路仅 B,对比 Replay 分支差异。理解 Fork 非 Spawn。

实验 6(Day 6):Spawn 子 Agent 完成「统计 docs 目录 md 文件数」,限制 tools 只 read/list。父 Agent 仅收 { count: number }。若子 Agent 返回全文,调整 outputSchema。

实验 7(Day 7)dsh plugin pack,在干净容器 Host 安装 tgz,不 link。跑全量 smoke。写 README 兼容性矩阵。将插件 topic 标 dsh-plugin 推 GitHub。

每实验对应 技能实验室 可视化模块;实验失败先查插件树与 graph,再查 Session Replay,最后查 OTel trace。养成 可观测优先 习惯后,开发效率高于盲目改 prompt。

附录 V:术语表与文档交叉索引

术语含义相关文档
Cordis插件与 DI 内核intro
Effect可逆副作用本文 §1.6、§2
Bundle插件集合本文 §7
Profile环境配置本文 §8
PresetStandard/Code/Minimal/Creatorfrom-zero-to-one
Session Log事件溯源日志本文 §4.3、§11.3
Spawn新建子 Session本文 §9
ForkSession 分支本文 §9
sandbox安全边界本文 §4.4
MCP模型上下文协议本文附录 M
Teardowneffect 清理函数best-practices

绝对路径注册指插件 entry、schemaRoot 相对于 Bundle 根解析,避免 cwd 依赖。peerDependencies 声明兼容 Cordis/runtime,不 bundled 进插件包。描述工程指工具 name/description/Schema 优化模型调用成功率。三平面指 Host/Preset/Client 配置维度分离。最小插件集指测试 Bundle 仅含 core + mock + 被测插件。

阅读顺序建议:intro → getting-started → 本文 → best-practices → github-projects → from-zero-to-one 实操。遇到概念混淆先查 faq。参与社区前读 GitHub 项目导读 贡献节。

插件作者常混淆 deps(插件依赖)inject(服务依赖):前者决定挂载顺序,后者决定运行时服务是否存在。manifest 写 deps 不含 @dsh/plugin-llm 但代码 inject llm,会在部分 Preset 运行时失败——集成测试应覆盖 Minimal 与 Standard 两种 Profile。

ctx.get 与 ctx.inject 选择:必需能力用 inject,启动期失败快速;可选增强用 get,运行时降级。文档必须声明「需要某 Preset」若 inject 必需服务。

热重载路径下 Teardown 是否调用取决于 Host 实现;插件不应假设只有进程 exit 才卸载。所有 effect 必须 idempotent-ready:重复 setup 前先 assume 已 teardown 或 guard duplicate register。

工具 idempotent 指多次相同参数执行结果等价且不叠加破坏;读工具天然 idempotent,写工具应在 Schema 标注 false 并在审批层提示。Replay 测试 non-idempotent 工具需 mock 或 skip re-execute。

Session idcall id 区别:前者标识对话,后者标识单次工具调用;日志与 span 应同时携带便于 grep。

Bundle pin 在生产 mandatory;开发可用 link 与 caret 版本,但 CI 仍应 pin 防 drift。

Creator 预设不是「更强模型」,而是「更宽沙箱」;安全责任在部署者与审批策略,不在模型。

Client slot 渲染失败不应阻断 Host Session;UI 插件应 ErrorBoundary 隔离。

OpenTelemetry 不是可选项 for 生产插件;至少 tool execute 需 span。

兼容性矩阵 列:插件版本、runtime、Cordis、Preset、已知 issue 链接。

dsh plugin validate 是发布门禁,非建议;本地 git pre-push hook 可绑 validate + test。

社区插件 topic dsh-plugin 便于发现;见 github-projects 文档。

七天学会 子站 https://dsh.chenxiaoshivivid.top/ 提供实验卡与 lab 可视化,与本文附录实验一一对应。

DeepSeek Harness 官方仓库 deepseek-ai/deepseek-harness 为 SSOT;插件不应 fork 改内核长期 drift。

LangGraph/LangChain 对照见附录 Q;选型见 best-practices。

MCP serverDSH 插件 分工:前者提供工具进程,后者注册/审批/审计。

Spawn 并发 limit 防 DoS;Host config 设 maxConcurrentSpawnsPerSession。

Fork 数量 limit 同理,防 Session 树爆炸。

审批 fatigue 缓解:只读 auto、写 ask、删 deny 分层。

Effect 泄漏 头号插件 bug;mount/unmount 循环测试为 CI 必项。

Schema 双重校验:JSON Schema 与 execute 内 assert 互补。

子 Agent outputSchema 防上下文爆炸。

RPC 版本化 Client/Host 契约向后兼容。

supply chain:pack 发布 checksum,禁 postinstall 任意脚本。

red team Creator 预设下测 sandbox 逃逸。

Session Replay 调试三件套:插件树、graph、replay。

插件树 CLI:dsh host debug plugins --tree

依赖图 CLI:dsh plugin graph --format mermaid

日志级别 DSH_LOG=cordis:*,tools:*,session:*

TypeScript strictsatisfies Plugin 减少运行时类型错。

模块 augmentation 类型安全 inject 键。

json-schema-to-typescript 从 Schema 生成 execute 参数类型。

Vitest 测 mount/unmount;Playwright 测 Client E2E。

createTestHost 轻量集成测试 Host。

mock llm 插件 替换真实 llm 服务键。

bundle.test.yaml 测试专用 Bundle 清单。

docker 多阶段 build pack + runtime add tgz。

semver major breaking、minor feature、patch fix。

CHANGELOG 标注 Preset 影响面。

SECURITY.md 插件漏洞披露邮箱。

good first issue 从文档与示例插件贡献入手。

RFC 大改内核先 RFC 再代码。

无特权核心 能力皆可插件卸载;读 architecture.md。

Append-only Session 不删改历史;压缩是衍生视图。

Resume 断点续跑;Replay 重现;Fork 分支尝试。

Preset 对比实验室 子站 lab preset-lab 并排 Token 与工具轮次。

审批模拟器 子站 lab approval-sim 测策略链。

内核可视化器 子站 lab 看挂载动画理解 Cordis。

毕业大作业 Day 7 发布可安装插件 + README 矩阵。

插件市场/挑战赛 子站 community 页。

Discord 社区入口见官方 README。

OpenClaw 网关 vs DSH 运行时互补。

OpenHands 沙箱参考;e2b 云沙箱参考。

Continue/Aider IDE/CLI Agent 参考工具描述。

AutoGen/CrewAI 多 Agent 对话模式参考。

Semantic Kernel 企业 Plugin 模型参考。

Mastra TS workflow 参考。

Phoenix/Arize OTel 追踪评估参考。

官方 npm 包 以仓库 Release 为准 pin 版本。

staging → canary → prod 插件升级三步。

回滚 保留 bundle git tag。

GDPR 日志脱敏;Session 导出需权限。

多租户 spawn 限流 + 工具 allowlist per tenant。

K8s resource limit + liveness 探针。

secrets 环境变量 inject,勿 commit .env。

Creator VM 快照 测试宽沙箱。

Minimal CI 无 API key 集成测。

Standard 手测 发版前人工对话测工具。

Code 预设 偏编码工具集;Standard 通用。

Patch 局部覆盖 单插件 config 不改整个 Bundle。

protected service keys 防社区覆盖 core tools/llm。

lazy getter 破环 inject 循环依赖模式。

event bus namespaced 防 listener 冲突。

child ctx 插件包子作用域。

tool namespace native vs mcp prefix。

approval explain 调试 decide 链。

SIEM audit org 基线插件写 Session 事件。

compress event 长会话插件 hook。

idempotent replay 配置是否 re-execute 工具。

webpack/vite external Cordis 不打包进 bundle。

tsup/esbuild 常见插件构建工具。

copy-schemas.mjs 构建复制 JSON Schema。

dsh.plugin.json manifest SSOT。

peer @cordis/core@dsh/runtime 双声明。

graph 环检测 启动期 fail fast。

watch mode link 后 Host 热重载开发。

pre-push hook validate+test。

Dependabot 插件依赖安全更新。

threat model README 段 告诉用户插件能访问什么。

五问评估社区插件 见 github-projects。

fork 实验 exp/ 分支 见 github-projects。

GitHub Projects 路线图 管理多插件版本列车。

兼容性矩阵 README 表格 四列起步:插件/runtime/Cordis/Preset。

贡献 PR 单一职责 插件与内核分 PR。

Issue 模板 含版本/Replay id/期望实际。

Discussions 功能建议先讨论再 RFC。

Release RSS 订阅 breaking。

文档 drift 以代码为准提 docs PR。

本站中文教学 + GitHub 英文 API 互补。

DeepSeek Harness 插件运行时工程 完成标志:可卸载、可测、可观测、可发布——本文 §14 DoD 自检全部打勾即可宣称掌握 Cordis 插件开发。

附录:从零实现一个可发布插件的完整走查

W1. 目标

实现 notify-dingtalk 插件:在任务 DONE 时按 webhook 发送摘要;必须可 Teardown;必须可被 deny;必须有 Vitest。

W2. 包结构

packages/notify-dingtalk/
package.json # peerDependencies: cordis / dsh 范围
src/index.ts # apply(ctx)
src/schema.ts # 配置 Zod/校验
test/lifecycle.test.ts
README.md # 兼容性矩阵 + dsh-plugin 说明

W3. apply 骨架要点

  1. inject: ['sessions'](或事件总线等价物);
  2. ctx.on('session.done', handler) 并用 effect 注册;
  3. webhook URL 只来自配置/密钥管理,不来自模型参数;
  4. 卸载时 abort 进行中的 fetch。

W4. 失败注入测试

用例期望
mount/unmount 循环 50 次无残留监听
webhook 500写入错误事件,不抛垮 Host
配置缺 URL启动失败且错误可读
审批 deny 通知不发送外网请求

W5. 描述与安全

不要把该能力做成「模型可任意传 URL 的通用 HTTP 工具」;那是 SSRF 温床。通知插件应绑定租户级 webhook。

W6. 打包与安装

使用绝对路径或 registry 包名在 Patch 中引用;相对路径是课堂常见翻车点。干净容器安装 tgz,禁止只在开发机 pnpm link 验收。

W7. 调试顺序

插件树 → 依赖图 → 事件是否触发 → Session 是否有副作用记录 → 网络层。先怀疑自己的 Teardown,再怀疑内核。

W8. Client Slot 可选增强

若要在 UI 显示「通知已发送」,通过 Client Plane 的 slots 注入只读徽章;不要从浏览器直连 webhook。

W9. Spawn 场景下的行为

子 Agent DONE 是否通知?默认只订阅父会话或显式配置 notify.on = ['root'],避免通知风暴。

W10. 发布检查表

  • peerDependencies 正确
  • README 含 Profile 示例
  • 兼容性矩阵(DSH 版本)
  • GitHub topic: dsh-plugin
  • 无密钥进仓库
  • CI:lifecycle + deny 用例

W11. 工具描述工程实验记录模板

记录:原 description、改写后 description、同 prompt 下选中率、错误参数率。没有这张表,就无法科学迭代工具 Schema。

W12. 与 MCP 工具的协作

本地插件适合低延迟强一致;MCP 适合跨语言/跨团队工具。同一能力不要双注册,否则模型会在两个近义工具间抖动。

附录:把「散落要点」收成一条开发主线

许多读者反馈开发指南像「要点清单」。用下面这条主线串起来,阅读与实践都会更稳:

  1. 契约层apply(ctx)、deps/inject、provide —— 决定插件能否进 Host。
  2. 副作用层effect / Teardown —— 决定能否安全热重载与卸载。
  3. 能力层:工具 Schema、审批策略、沙箱边界 —— 决定模型「能动什么」。
  4. 证据层:Session 事件、Replay、OTel —— 决定出问题时能否定位。
  5. 交付层:Bundle pin、兼容矩阵、dsh-plugin 发布 —— 决定别人敢不敢用。

一周开发节奏(建议)

焦点完成定义
Mon最小插件骨架 + lifecycle 测试mount/unmount 50 次无泄漏
Tue工具注册 + 描述工程同 prompt 稳定选中工具
Wed审批与 deny 用例Session 可见 deny 事件
ThuReplay fixture + mock LLMCI 可复现
Fripack/install 干净容器验收不依赖 pnpm link
SatOTel span + README 矩阵可交接
Sun对照七天学会实验卡复盘笔记进团队库

反模式速查

  • apply 顶层启动不可停的后台任务;
  • @cordis/core 打进插件 bundle;
  • 用相对路径注册生产插件;
  • 只在开发机 pnpm link 验收;
  • 工具 description 写成「通用助手」导致模型乱选;
  • 无 Replay 就宣称「兼容新 runtime」。

把本附录与正文 §1–§14 对照阅读:正文给机制,本附录给节奏。遇到散落 bullet,先问它属于契约/副作用/能力/证据/交付哪一层,再决定写进哪份 PR 检查表。