Back to skills
SKILL.md
dsh-plugin-dev-skill
ASecurity指导任何 Agent 正确、高效、符合规范地开发 DeepSeek Harness(DSH)插件。涵盖 Tool(defineTool)、LLM 适配器、服务与依赖、事件系统、配置、打包发布,以及 Cordis 框架的心智模型、代码模板与验证清单。
- 5 stars
- 0 votes
- 0 copies
- 2 views
- Added September 30, 2026
Works with
Security analysis
92/100- Installs packages at runtime which could introduce malicious dependencies
Pro scans all 19 files and shows the line behind each finding
npx -y skills add green-dalii/dsh-plugin-dev-skill --agent claude-codeAre you the author of dsh-plugin-dev-skill?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/green-dalii-dsh-plugin-dev-skill)---
name: dsh-plugin-dev-skill
description: 指导任何 Agent 正确、高效、符合规范地开发 DeepSeek Harness(DSH)插件。涵盖 Tool(defineTool)、LLM 适配器、服务与依赖、事件系统、配置、打包发布,以及 Cordis 框架的心智模型、代码模板与验证清单。
whenToUse: 当任务涉及为 DeepSeek Harness 编写/修改/调试插件(tool、LLM adapter、服务提供方、钩子、UI、协议桥等),编写或修改 cordis.yml / cordis.patch.yml / dsh.profile / dsh.bundle 配置,使用 dsh plugin 命令,或需要理解 ctx.tools、ctx.llm、ctx.agents、ctx.sessions 等服务与 tools/*、agent/*、session/event 等事件时,加载本技能。
metadata:
version: 0.6.2
upstream: https://github.com/green-dalii/dsh-plugin-dev-skill
sdk-baseline: 0.2.1-alpha.1
---
# DeepSeek Harness Plugin Dev Skill
> 让任何 Agent 都能正确、高效、符合规范地开发 DeepSeek Harness(DSH)插件。
> 本技能是 DSH 插件开发的操作手册:先给出心智模型与铁律,再给出可直接照抄的代码模板与分步流程,最后给出验证清单。深度背景见 `References/` 目录下的精简提炼文档。
>
> ⚠️ **载入本技能后,先执行 §0.1「检查 skill 是否最新」**:不是最新版就先更新,再开始使用本技能。
>
> **SDK 基线**:本文 API 陈述以 `@deepseek-ai/*` **0.2.1-alpha.1**(cordis **4.0.5-alpha.1**)的类型定义实测为准。0.2.0 把过去几处「文档领先于 SDK」的项正式落地了:PTC 运行时 seam 定名 **`ctx.ptcRuntime`**(原 `ctx.codeRuntime`,包 `dsh-ptc-runtime` / 提供方 `dsh-ptc-runtime-node`)、**Plugin Manager**(`plugin_manager`)随发行版交付、E2B 系提供方移除并由 SSH 家族接管远程能力。0.2.1-alpha.1 主要是 Web 体验与若干 bug 修复(**对 tool / LLM / service 插件的公共 API 0 破坏**),并继续清理了上游诊断路径:`@deepseek-ai/dsh-invariants` 与 `<pkg>/invariant` 子路径发布已删除(详见 §12)。升级 SDK 后请按各 `References/` 文件末尾的官方链接复查。
---
## 0. 何时使用本技能
当任务涉及以下任意一项时,必须加载本技能:
- 为 DeepSeek Harness 编写、修改或调试**插件**(tool、LLM adapter、服务提供方、钩子、UI、协议桥等)
- 编写或修改 `cordis.yml` / `cordis.patch.yml` / `dsh.profile` / `dsh.bundle` 配置
- 用 `dsh plugin ...` 安装、移除、打包插件
- 理解 DSH 中 `ctx.tools`、`ctx.llm`、`ctx.agents`、`ctx.sessions` 等服务或 `tools/*`、`agent/*`、`session/event` 等事件
- 研究 DSH 的插件模型(Cordis 框架)如何工作
先读本 SKILL.md 全篇,再动手。动手前先读项目里已有的 `cordis.yml` / profile / 相关源码,确认现有约定。
### 0.1 载入后第一件事:检查 skill 是否最新(强制流程)
**本技能会随官方 DSH 演进而更新。每次载入本技能后,先确认自己用的是最新版;若不是最新版,先更新、再开始用它干活。**
理由很实际:上游有过破坏性改名(`Code Mode` → PTC mode、`CallId` → `ToolCallId`、`ctx.codeRuntime` → `ctx.ptcRuntime`、profile manifest 删除 `patchReload`),用过期版本会写出**编译不过或行为不对**的插件。
1. **读本地版本**:`VERSION` 文件与本 `SKILL.md` 同目录,内容形如 `0.6.0`(frontmatter 的 `metadata.version` 是同值副本)。
- 若**没有** `VERSION` 文件(例如只拷贝了 `SKILL.md` 的残缺安装),视为**旧版本,直接执行第 4 步更新**。
2. **取远端版本**(一次约 5 字节的 GET,用短超时,不要拖慢主任务)—— 为了抗 `raw.githubusercontent.com` 的 CDN 缓存延迟(曾观测到刚 `git push` 后 5+ 分钟内 `main` 分支 raw URL 仍返回旧文件),**优先读 tag-pinned URL**(tag 不会被新提交覆盖,永远指向同一个发布):
```sh
# 1) 先问最新 tag(成本几乎为零)
TAG=$(git ls-remote --tags --sort=-v:refname https://github.com/green-dalii/dsh-plugin-dev-skill.git | awk -F/ '/refs\/tags\/v/ {print $3; exit}')
# 2) 用 tag 钉住的 raw URL 读 VERSION —— 这一份保证是「最新发布」而非「main 分支的 CDN 缓存」
curl -fsSL --max-time 8 "https://raw.githubusercontent.com/green-dalii/dsh-plugin-dev-skill/${TAG}/VERSION" | tr -d '[:space:]'
```
把拿到的远端版本记为 `REMOTE`。再读 `main` 分支 raw URL 拿到的版本记为 `MAIN`:如果两者**不一致**(CDN 延迟,main 落后于 tag),**以 tag-pinned 的 `REMOTE` 为准**—— `MAIN` 是「远端最终会稳定到的值」。若 `git ls-remote` 也失败,退而求其次:
```sh
curl -fsSL --max-time 8 https://raw.githubusercontent.com/green-dalii/dsh-plugin-dev-skill/main/VERSION | tr -d '[:space:]'
```
3. **比较(语义化版本)**:
- 远端 **高于** 本地 → 执行第 4 步,更新完再继续。
- 两端**相同** → 直接用。
- 远端 **低于** 本地 → 说明本地是开发中的更新版,**不要降级**,直接用。
4. **更新到最新版**,按安装形态二选一:
- **符号链接 / git 检出**(推荐形态):
```sh
cd "$(readlink -f <本 SKILL.md 所在目录>)" && git pull --ff-only
```
若该目录不是 git 仓库(纯拷贝安装),改用下一条。
- **普通拷贝**:
```sh
git clone --depth 1 https://github.com/green-dalii/dsh-plugin-dev-skill /tmp/dsh-plugin-dev-skill-new
rsync -a --delete --exclude .git /tmp/dsh-plugin-dev-skill-new/ <本 SKILL.md 所在目录>/
```
更新后**重新读取 `SKILL.md` 与本次要用到的 `References/`**——内容可能已变(尤其是 API、术语与包名)。
5. **无法联网 / 检查失败时**:不要卡住,也不要假装检查过。明确说一句「未能检查 skill 更新(原因)」,然后基于当前本地版本继续工作,并在需要新 API 时提示用户可能存在版本差异。
6. **同一会话内**:已成功比较过且远端版本未变时,可以跳过重复检查(不必每次工具调用都发请求);但**每次载入技能都要检查**。
7. **怀疑基线漂移时**:跑 `bash scripts/verify.sh`(在仓库根目录)做一次完整复核——版本三处一致、SKILL.md frontmatter 合法、官方链接全部 200。CI 也会跑它。
> 检查成本是一个几字节的 GET,而用过期技能写出编译不过或不符合规范的插件,代价要高得多。
---
## 1. 心智模型:DSH 是构建在 Cordis 之上的插件化系统
一句话概括:**DeepSeek Harness 是一个 Agent Harness SDK,其中每一项能力——工具、LLM 适配器、文件访问、agent loop 本身——都是一个插件(component),挂载到一个共享的上下文(context)上。**
**没有特权内核**:DSH 没有「核心源码」可供打补丁——扩展方式是挂载插件,而不是修改循环。工具(`dsh-tools`)、LLM 运行时(`dsh-llm`)、文件系统(`dsh-fs`)、沙箱、持久化,乃至 **agent loop 本身**都是可替换的插件;换掉某个能力通常只是改 `cordis.yml` 里的提供方。
随发行版交付的可启动 profile:`web`、`headless`、`sdk`、`sdk-minimal`、`acp`(`desktop` 被 CLI 保留但不直接启动)。用户自有 profile 位于 `$DSH_HOME/profiles/<name>`。
**三个事件域**(动手前第一个决定就是选对事件域):① **会话事件**——`session/event` 中持久化的 `turn/*`、`step/*`、`tool/call`、`tool/result`;② **agent 事件**——`agent/*`,围绕一次 agent 生命周期与步骤边界;③ **能力事件**——`tools/*`、`llm/*`、`fs/*` 等子系统各自的扩展点。术语:**步骤(step)= 一次模型请求及其工具调用**;**轮次(turn)= 0..n 个步骤**,以「没有待执行工具调用」结束。
三个核心概念(来自论文《A Programming Paradigm for Spatiotemporal Composability》,见 `References/10-spatiotemporal.md`):
| 概念 | 含义 | 在代码中的体现 |
|---|---|---|
| **可逆效应(revertible effect)** | 组件对共享环境的每次修改都携带一个逆操作,运行时跟踪并在组件卸载时按 LIFO 顺序恢复 | `ctx.effect(() => () => cleanup)`;`ctx.on()`、`ctx.tools.register()` 等都是 effect,卸载自动撤销 |
| **反应式余效应(reactive coeffect)** | 组件声明它依赖哪些服务;服务出现/消失/换实现时,组件自动激活/停用/重载 | `export const inject = ['tools']`;依赖消失时插件自动 dispose,恢复时自动重载 |
| **Fiber(组件实例)** | 每个已加载的插件实例是一个 fiber,有生命周期状态机(cordis 4.0.4 起:`PENDING` → `LOADING` → `ACTIVE` → `UNLOADING` → `DISPOSED`,`apply` 抛错则进入 **`FAILED`**) | `await fiber.dispose()` 卸载;`fiber.update(config, noSave?)` 重启(**返回 `void`,要等完成调 `await fiber.await()`**) |
由此得到的**铁律**:
1. **所有注册都必须通过 `ctx` 完成**(`ctx.on` / `ctx.tools.register` / `ctx.effect` / `ctx.plugin` / `ctx.provide`)。凡是经过 `ctx` 的注册,卸载时自动清理,**绝不手动 removeListener / clearInterval**。
2. **凡是不经过 `ctx` 管理的资源**(网络连接、自建定时器、文件句柄、watcher),必须包在 `ctx.effect(() => { ...; return () => cleanup })` 里。
3. **依赖必须声明**(`inject`),不要用 `ctx.get()` 探测必需依赖,不要臆想加载顺序——顺序由依赖决定,不由 `cordis.yml` 行序决定。
4. **配置必须可配置**:任何不同部署取值可能不同的参数都必须成为 `Config` 字段,绝无硬编码(检验标准:能否在 `cordis.yml` 中改这个值而不改代码?)。
5. **配置错误要响亮**:用 Schemastery schema 在加载时校验,无效配置直接加载失败并给出明确错误,绝不带病运行。
6. **回调可被回放/热重载**:展示类回调(`output.render`、`presentCall`、`presentResult`)必须是纯函数;插件随时可能被 HMR 卸载重载,不要依赖跨重载的全局状态。
---
## 2. 环境与工具链
- 插件是 **TypeScript 模块**(也接受 JS),运行于 Node.js ESM 环境。
- 核心依赖包(都来自 `@deepseek-ai` 作用域):
- `@deepseek-ai/cordis` — 插件框架(context、fiber、事件、注册表)
- `@deepseek-ai/schemastery` — 配置 schema 校验(Standard Schema 实现)
- `@deepseek-ai/dsh-tools` — `defineTool` 与工具注册表服务
- `@deepseek-ai/dsh-llm` — `LlmAdapter`、`StreamChunk`、`GenerateOptions`、`ToolCallId`(注意:旧名 `CallId` 已废弃,SDK 不再导出)
- 各能力 seam 包:`dsh-shell`、`dsh-session`、`dsh-agent`、`dsh-system-prompt`、`dsh-fs`、`dsh-jobs`、`dsh-credentials` 等(完整清单见 `References/08-capability-layering.md`)
- 两个开发入口(任选):
- **源码检出**(推荐,教程场景):克隆 `deepseek-ai/deepseek-harness`,`pnpm install`,用 `pnpm dsh web --patch <你的cordis.yml>` 启动 Web UI,用 `node --import tsx ../../vendor/cordis/bin.js` 跑纯 Cordis 教程示例。
- **已安装 CLI**:`dsh --profile <name> ...` 启动 profile,`dsh plugin --profile <name> <pnpm args>` 管理插件。
---
## 3. 插件的最小形态(先学会这个)
一个插件就是一个导出 `apply` 函数的模块:
```ts
import type { Context } from '@deepseek-ai/cordis'
export const name = 'my-plugin' // 可选,仅用于诊断显示
export function apply(ctx: Context) {
// 在这里注册能力
}
```
通过 `cordis.yml` 或 patch 挂载:
```yaml
- name: './src/my-plugin.ts' # 相对路径(源码/开发)或 npm 包名(安装后)
id: my-plugin # 稳定标识,loader 按 id 做配置对账
```
### 三种插件形态
1. **函数形态**(默认推荐):
```ts
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) { /* ... */ }
```
2. **对象形态**:`export default { name, inject, apply(ctx) {} }`
3. **类形态**(需要对外提供服务时):`export default class MyService extends Service { static inject = [...]; constructor(ctx) { super(ctx, 'myService') } }`
三种形态共享同一组静态元数据:`name`、`Config`、`inject`、`provide`、`intercept`;类形态用 `static inject` / `static provide`。
**带配置的形态**:`export function apply(ctx: Context, config: Config)` —— 第二个参数是校验后(已填默认值)的配置对象,见 §5。
> 在需要提供服务之前一律用函数形态。
**加载失败语义**:`Config` 校验失败时插件以 `ValidationError` 停在 `FAILED`,`apply` 永不执行——所以「配置错误要响亮」是免费的,前提是你真的声明了 schema。
**HMR 提醒**:默认只热重载**配置**;插件模块(代码)变更通常仍需重启进程(要开源码热替换,用 profile patch 改 `dsh-hmr` 行的 `root`,见 `References/07-publish.md`)。
---
## 4. 开发一个 Tool(最高频任务)
工具注册到 `ctx.tools`,用 `defineTool` 定义。**模板(可直接照抄):**
```ts
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'my-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'my_tool', // 模型看到的工具名(snake_case 惯例)
description: 'One sentence about what it does.', // 模型看到的功能描述,要具体、说清何时用
parameters: {
path: { type: 'string', required: true, description: 'What the param means' },
limit: { type: 'number' }, // 未标 required 即为可选
},
output: {
schema: { type: 'string' }, // 声明 execute 返回的规范 JSON 值
render: (_args, value) => [{ type: 'text', text: value }], // 转为模型可见内容
},
async execute(args, exec) {
// args 已被 schema 校验并推断类型
// exec.signal 是取消信号,长任务必须响应它
return `result for ${args.path}`
},
}))
}
```
### defineTool 的完整契约(务必遵守)
- **`parameters`**:参数 schema 映射,根是隐式开放对象;每属性用 `required: true` 标注必填(**只能写 `true` 或省略**——写 `required: false` 会类型报错)。支持 `string`/`number`/`integer`/`boolean`/`null`/`array`/`object`/`json`(无约束的任意 JSON 值)/`oneOf`(至少 2 个分支)。
- **嵌套 object 节点**:写 `{ type: 'object', properties: { ... }, additionalProperties: false }`——`additionalProperties` **必须显式声明**(否则会拿到意外的 JSON Schema 默认开放语义);并且**没有 `required: [...]` 数组**,必填同样靠每个属性自己的 `required: true`。
- **`output.schema`**:声明 `execute` 返回的规范 JSON 值(对象/数组/标量/null 皆可)。这是给**程序化调用方**(PTC mode 的 `await tools.xxx()`)用的 API,要设计成直接返回句柄与字段;**面向人的解释放 `output.render`**。
- **`execute(args, exec)`**:
- args 已经过运行时校验,但 schema DSL 表达不了的约束(非空字符串、正数、跨字段)要自己 `throw new Error(...)`。
- 返回 `output.schema` 声明的规范值。**不要返回内容块**,不要逼调用方从自然语言里解析 id。
- 遵守 `exec.signal`:信号触发时取消进行中的工作。
- 基础设施故障(抛异常/无效返回值)→ 结果标记为 `isError`。**成功但结果不理想**(如非零退出码)→ 仍返回规范值,在 render 里解释。
- 组合工具可额外用 `exec.deferContext(msg)` 把上下文延迟到本工具 `tool/result` 之后再交给循环(按调用序),或用 `exec.concludeTurn()` 标记本成功结果为当前轮次终点。
- `exec.schema`(0.2.0 新增)是 PTC 内层调用在**绑定时刻**冻结的工具 schema,永不写入日志。
- **`output.render(args, value)`**:纯函数,把规范值转成 `ContentBlock[]`(模型看到的文本)。绝不在此做 I/O、读会话状态。
- **可选 `output.presentationMeta(args, value)`**:从规范值派生可回放的 UI 元数据(纯函数)。
- **可选 `presentCall(args)` / `presentResult(args, result)`**:声明工具在 UI 中的卡片渲染意图。`presentCall` 可用 `'generic' | 'terminal' | 'diff'`;`presentResult` 额外支持 `'search' | 'web' | 'read'`。必须**纯函数**(会在实时流和会话回放中运行),不做 I/O。
- **定义级运行时元数据与内容变换**:`timeoutMs`(协作式超时预算,由 `@deepseek-ai/dsh-tool-call-timeout-policy` 强制)、`isConcurrencySafe(args)`(**只有精确返回 `true`** 才允许与兄弟调用并行,否则独占并构成排序屏障)、**`projectContent(exec, result)`**(0.2.0 新增:把执行期准备好的文本/图片在 `tools/post-execute` **之前**装进去,可被后续策略覆盖)、**`finalizeContent(exec, result)`**(`tools/post-execute` **之后**的最后一道内容变换,对每个归一化结果恰好一次——**包括**绕过 post-execute 的流水线失败——必须全函数、不抛错)。后两者返回 `ContentBlock[] | undefined`。
- **模型可见字段**:`name` / `description` / `parameters`,以及 0.2.0 新增的 **`deferLoading?: true`**(该工具延迟加载,模型按需激活,而不是每次请求都下发完整声明列表)。上面这些运行时字段与回调**都不会进入模型请求**。语义细节见 `References/03-tools.md` §2。
- 参数不可变:把 `args` 当只读;注册后不要改 schema 或替换回调。
### 长时间运行的任务(后台任务)
若要支持 `run_in_background`:
```ts
const jobs = ctx.get('jobs')
if (!jobs) throw new Error('background jobs unavailable: load @deepseek-ai/dsh-jobs and @deepseek-ai/dsh-tool-jobs')
return {
kind: 'background',
jobId: jobs.start({
kind: 'my-work',
label: args.command,
...exec.agent ? { owner: exec.agent.id } : {}, // ⚠️ 0.2.0:owner 是 SessionId,不再是 agent 对象
output: processSources, // 可选:注册表拉取式输出源
run: (job) => { // ⚠️ 0.2.0:run 接收 JobHandle
job.append('progress line\n') // 也可用 job.updateProgress(line)
return { cancel: (reason) => { /* ... */ }, done } // ⚠️ 0.2.0:JobHooks 已无 readOutput
},
}),
}
```
要点:发布 jobId 之后用任务自己的取消信号(归 `job_kill` / owner dispose 管),不再用 `exec.signal`;成功输出 schema 要能承载 `{ kind: 'background', jobId }` 这种规范句柄;任务结果用 `JobOutcome.result`(旧名 `output` 已改)。参考 `References/03-tools.md` 与 `@deepseek-ai/dsh-tool-bash` 实现。
### 工具的执行策略(扩展点,按需用)
| 扩展点 | 模式 | 用途 |
|---|---|---|
| `tools/pre-execute` | waterfall | 允许/拒绝/取消/询问策略(权限门禁);返回 `{kind:'allow'}` / `{kind:'deny', reason, info?}` / `{kind:'cancel'}` / `{kind:'ask', reason?, displayReason?}` |
| `ctx.tools.guard()` | 单调守卫 | 最终拒绝,后续监听器无法撤销(返回 reason 即拒绝) |
| `tools/execute` | waterfall | 包裹分发:超时/重试/指标;可替换 `exec.signal` 但不可移除 |
| `tools/post-execute` | waterfall | 替换内容/值、阻止结果、附加模型上下文 |
| `tools/ptc-dispatch-log` | waterfall | 改写 `run_code` 子分派**持久日志副本**的内容(返回 `ContentBlock[]`;程序本身已收到完整值) |
| `tools/result` | emit | 只读观测不可变最终结果(日志、审计) |
| `tools/change` | emit | 工具集变化通知(不受作用域过滤;影响每个 agent 的下一次装配) |
策略不要内建进工具;把部署策略放进钩子插件(普通 Cordis 插件监听这些事件即可,如权限门禁示例见 `References/11-cookbook.md`)。
需要**同一进程内让不同 agent 用不同呈现方式**(PTC 与 native 并存)时,用 `ctx.tools.presentAs('native' | 'ptc' | 'both')` 按作用域声明,就近作用域胜出;进程级默认值走 `dsh-tools` 的 `mode` 配置。
---
## 5. 插件配置(Schemastery)
插件导出 `Config`(同名的 TS 接口 + Schemastery schema),Cordis 在 `apply` 前校验并填充默认值:
```ts
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
export interface Config {
greeting: string
maxRetries: number
verbose?: boolean
}
export const Config: Schema<Config> = Schema.object({
greeting: Schema.string().default('Hello'),
maxRetries: Schema.number().default(3),
verbose: Schema.boolean().default(false),
})
export function apply(ctx: Context, config: Config) {
// config 一定是完整且经过校验的
}
```
在 `cordis.yml` 中传配置:
```yaml
- name: './src/my-plugin.ts'
config:
greeting: 'Hi there'
maxRetries: 5
```
要点:
- **`Config` 必须是一个 Standard Schema 对象**(用 Schemastery 构建),不能导出普通对象——Cordis 只接受 Standard Schema 接口。
- **校验必须是同步的**:schema 返回 Promise 会让 Cordis 抛 `TypeError: Async config validation is not supported`。`Config` 也可以整体省略(等于无校验、无默认值、配置原样透传)。
- 校验失败 → fiber 停在 `FAILED`,`apply` 不执行,CLI 非零退出。错误信息形如 `$.targets expected array but got not-an-array (at targets)`。
- 常用构造:`Schema.string().required()` / `.default(x)`、`Schema.number()`、`Schema.boolean()`、`Schema.array(String)`、`Schema.union(['a','b'])`、`Schema.object({...})` 嵌套。
- 支持 `!!js` 表达式在加载时求值(如 `greeting: !!js process.env.GREETING ?? 'Hello'`),在 `config` 内**递归生效**;`!!js` 仅对 `config` 与 `disabled` 字段有效。注意 `--dump-config` 原样打印 `!!js` 而不求值。
- 配置变更触发旧实例卸载(注册是 effect,自动清理)→ 新实例加载。**不要在插件外部缓存配置**。
- HMR 现在由 **YAML 组合**决定(0.2.0 起 profile manifest 的 `patchReload` 字段已删除,残留该键无任何效果):`dsh-base` 默认挂 `@deepseek-ai/dsh-hmr` 且 `root: []`,故 base 系 profile(含 `web`)**配置热重载默认开、源码模块热替换仍需 opt-in**(用 profile patch 把 `root` 改成 `["."]`);`headless`/`sdk`/`acp` 显式 `disabled: true`(仅启动时加载),`sdk-minimal` 没有 hmr 行。**0.2.1 起**:开启 dev directory 监听后,HMR 还能在源码变更时刷新**包入口与依赖映射**,并正确重载仍在运行的原入口——配置层以外的二次重启通常不再需要。
- 想让配置出现在 Web「插件配置」页:用 `ctx.settings.installSection(...)`,细节见 `References/04-config.md` §7。
---
## 6. 服务与依赖(服务端开发)
### 使用服务(消费方)
```ts
export const inject = ['tools'] // 必需依赖:不满足就不加载
export function apply(ctx: Context) {
ctx.tools.register(/* ... */) // apply 时保证已就绪
}
```
`inject` 也支持对象形式(服务名 → 拦截配置)。**可选依赖**:不写 `inject`,使用时 `ctx.get('service')` 探测(`undefined` 表示没有可用提供方;`strict` 默认 `true`,只返回提供方 fiber 处于 `ACTIVE` 的实现)。
### 提供服务(提供方)
用 `Service` 子类(类形态插件):
```ts
import { Service, type Context } from '@deepseek-ai/cordis'
declare module '@deepseek-ai/cordis' {
interface Context {
metrics: MetricsService // 声明合并:让 ctx.metrics 有类型
}
}
export default class MetricsService extends Service {
static inject = ['llm'] // 服务也可以依赖其他服务
constructor(ctx: Context) {
super(ctx, 'metrics') // 'metrics' 即服务名
}
record(event: string, value: number) { /* ... */ }
}
export const name = 'metrics'
export function apply(ctx: Context) {
ctx.plugin(MetricsService)
}
```
**轻量替代(无需写类)**:`ctx.provide(name, value, check?)` 直接注册服务实现(effect,卸载自动注销)。第三个参数 `check` 是可用性谓词,依赖方据此判断是否就绪——需要「已注册但暂时不可用」的中间态时用它。
其他低层 API:`ctx.set/get` 读写存储(对未提供的名字 `set` 会抛错)、`ctx.accessor` 定义计算属性、`ctx.mixin` 把服务成员挂到 ctx 上。
### 服务行为语义(理解而非背诵)
- `inject` 不是一次性启动检查:运行期间必需服务消失 → 依赖插件自动 dispose;服务恢复 → 自动重载。这保证消费方永远不会持有对已卸载服务的引用。
- 服务名共用**扁平命名空间**:`tools`、`llm` 等已被占用,自有服务名要加辨识度前缀(如 `myCap`、`metrics`)。**服务名与公开方法以子系统页面生成的 `cordis-surface` 区块和 TS 接口为准,不要自己维护静态清单。**
- **服务隔离**:`cordis.yml` 中用 group + `isolate` 让不同插件组看到同一服务名的不同实例(如两组各自配置不同 timeout 的 `shell` 提供方);隔离后同名服务按 scope 分别解析。
---
## 7. 事件系统(插件间通信)
### 分发模式(事件名的一部分,必须选对)
| 模式 | 调用 | 语义 |
|---|---|---|
| `emit` | `ctx.emit(name, ...args)` | 同步广播,不等待、不收集返回值 |
| `parallel` | `await ctx.parallel(name, ...args)` | 所有监听器并发运行并等待(返回 `Promise<void>`) |
| `serial` | `await ctx.serial(name, ...args)` | 按序 await,首个非 null/false/undefined 返回值胜出并终止后续 |
| `bail` | `ctx.bail(name, ...args)` | serial 的**同步**版,返回首个 bail 值(不是 Promise) |
| `waterfall` | `await ctx.waterfall(name, ...args, next)` | 环绕中间件;**调用方必须把「最内层延续(默认行为)」作为最后一个实参传入**,分发器取出后为每个**监听器**替换成链式 `next`;返回最外层监听器的返回值 |
**waterfall 铁律:只观察/标注的监听器必须调用 `next()`;不调用 `next()` 直接返回 = 有意短路(否决/拦截)。** 忘记调用 `next()` 会静默吞掉下游默认行为;而**调用方漏传最后一个 `next` 实参**,则会让最后一个真实参数被当作默认行为而抛 `TypeError`(两侧都容易出错,务必区分)。
### 类型安全的事件
```ts
declare module '@deepseek-ai/cordis' {
interface Events {
'my-plugin/ready': (payload: { id: string }) => void
'my-plugin/check': (input: string) => boolean | undefined
}
}
```
事件命名约定 `namespace/action`(如 `agent/step`、`tools/result`、`session/event`)。监听器用 `ctx.on()` 注册即自动随插件卸载清理(返回 disposer);`ctx.once()` 只触发一次;选项支持 `{ prepend: true }`(插到现有监听器之前)与 `{ global: true }`(跳过作用域过滤)。
### 作用域过滤分发(最容易踩的坑)
Harness 中「关于某个 agent 的活动」的事件(`agent/*`、`tools/*`、`session/*`、`system-prompt/assemble`)以该 agent 的 **scope carrier** 作为 `thisArg` 分发,监听器按注册上下文的作用域标签过滤:
- 事件签名写作 `(this: Scoped<Agent>, payload)`;分发方传 carrier(`ctx.emit(scopeTarget(agent, agent), 'agent/status', payload)`)。
- 无标签监听器(普通 `ctx.on`)收到全部事件;带标签监听器(在 `agent.ctx` 上注册)只收到**本 scope 及其祖先链**的事件——事件向上流动,绝不向下。
- 注册表主体事件(如 `tools/change`)**有意不过滤**:全局变化对所有 agent 的下一次装配都成立。
- 关键区别:通过 `agent.ctx` 注册只决定 **effect 的作用域归属**;分发是否过滤始终取决于分发方是否传 carrier。
### 自己发事件时(生产方契约)
- 命名 + 在声明处标 `@mode`,只按声明的模式分发(类型系统强制方法名)。
- **隔离监听器异常**:一个抛错的订阅者不得 reject 分发 promise,也不得饿死后面的监听器——分发循环用 try/catch 包裹并记录。
- payload 视为只读且可无损 JSON 序列化;`emit` / `bail` 是同步的,不要在其中做异步工作。
### 常用 Harness 事件(开发时经常用到)
- `tools/pre-execute` / `tools/execute` / `tools/post-execute` / `tools/ptc-dispatch-log` — waterfall;`tools/result` / `tools/change` — emit(工具流水线见上表)
- `agent/request`(waterfall,替换冻结的模型调用配置)、`agent/request-error`(waterfall,单次请求失败的重试策略)、`agent/pre-step`(waterfall)、`agent/created`(**serial**;0.2.0 起 `agent/session-start` 已删除并合并进它,创建 resolve 前被 await,抛错会失败创建)、`agent/assistant-stream`(emit,进程内实时流分片)、`agent/turn-stopping`(serial,轮次停止边界,异议者用 `agent.steer()` 继续跑)
- `session/event` — 持久化的会话事件流(`turn/*`、`step/*`、`tool/call`、`tool/result`、`assistant/chunk` 都是这里的 `event.type`,**不是**同名 Cordis 事件);另有 `session/created` / `session/disposed`(emit)、`session/flush`(parallel)
- `approval/request` — 审批 waterfall
- `system-prompt/assemble` — 系统提示词整体装配(作用域过滤;权威返回,监听者有责任保留既有贡献)
- **0.2.0 事件变动**:删除 `agent/session-start`(并入 `agent/created`)与 `settings/updated`;`agent/created` 由 emit 改 **serial**;新增 `app-boot/config-reload`、`hmr/change`、`hmr/reload`、`plugin-manager/*`、`workspace/session-activity`、`workspace/session-stop`、`deepseek-account/*`、`schedule/changed`、`permission-presets/catalog-changed`、`compaction/summary-error`、`connection/request`、`loader/volatile-update`
完整签名与分发模式见各子系统页面生成的 `cordis-surface` 区块(`References/08-capability-layering.md` 有指引);**谁派发、谁监听**查官方 `event-producer-consumer` 矩阵(该页未发布到文档站,链接见 `References/06-framework-services-events.md`)。
---
## 8. LLM 适配器(接入新模型提供方)
适配器 = 继承 `LlmAdapter`、实现 `stream()` 的类,把 Harness 提供方无关请求转成具体 API 调用并转回 `StreamChunk` 分片。
```ts
import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
class MyAdapter extends LlmAdapter {
async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
// 1. options.messages → 提供方格式
// 2. 调流式 API(必须传 options.signal)
// 3. 响应 → StreamChunk 序列
}
}
export const name = 'my-llm-adapter'
export const inject = ['llm']
export const Config = Schema.object({
apiKey: Schema.string().required(),
providers: Schema.array(String).required(),
})
export function apply(ctx: Context, config: Config) {
ctx.llm.registerAdapter(config.providers, new MyAdapter(config.apiKey))
}
```
### StreamChunk 协议(严格遵守)
```ts
import { ToolCallId, type StreamChunk } from '@deepseek-ai/dsh-llm'
async function* chunks(): AsyncIterable<StreamChunk> {
yield { type: 'block-start', index: 0, blockType: 'text' }
yield { type: 'text-delta', index: 0, text: 'Hello' }
yield { type: 'block-end', index: 0, block: { type: 'text', text: 'Hello world' } }
// 工具调用块:
yield { type: 'block-start', index: 1, blockType: 'tool-call' }
yield { type: 'tool-call-delta', index: 1, id: ToolCallId('c1'), name: 'bash', argumentsDelta: '{"command":"ls"}' }
yield { type: 'block-end', index: 1, block: { type: 'tool-call', id: ToolCallId('c1'), name: 'bash', arguments: '{"command":"ls"}' } }
yield { type: 'usage', usage: { inputTokens: 100, outputTokens: 50 } } // 必须在 finish 前
yield { type: 'finish', reason: { kind: 'stop' } } // 必须最后
}
```
规则:每个 `block-start` 必有对应 `block-end`;`index` 从 0 递增;`tool-call` 的 `arguments` 全程是**原始 JSON 字符串**;`finish` 是最后一块;`usage` 在 `finish` 前。
### 错误与元数据
- 传输/协议故障:`throw new LlmError(msg, '<CODE>', { status?, providerRetryAfterMs?, requestId?, offloadImages? })`,不要依赖普通 `Error` 自动转换。`code` 是**自由字符串**(SDK 不提供错误码联合类型),核心导出的规范常量有 `CONTEXT_WINDOW_EXCEEDED`、`QUOTA`、`ACCOUNT_QUOTA`、`EMPTY_RESPONSE`、`INVALID_CREDENTIAL`、`IMAGE_OFFLOAD_REQUIRED`。
- 提供方不支持 `GenerateOptions` 中的某个字段:`throw new LlmError(..., 'UNSUPPORTED_OPTION')`(该 code 是**适配器自有**约定,核心不导出),绝不静默丢弃。
- 每个 HTTP 请求合并 `attributionHeaders()`,并传递 `options.signal`。
- 可选:覆写 `resolveModel()`(返回提供方/模型身份 + 可选 `context`、`reasoning`、`systemPromptUpdate`、`toolUpdate` 与 **`inputModalities`**——未声明图片能力时运行时会主动把图片投影掉)与 `listModels()`(公布模型选项)。
- 可选:覆写 `prepareCall(provider, model, signal?)`,把连接事实绑定到**本次生成**,避免「能力解析」与「派发」之间配置变化导致混用两代配置(动态端点的适配器应覆写它)。
完整教程见 `References/05-llm-adapter.md`;参考实现:`packages/llm/llm-deepseek`(OpenAI 兼容 SSE)与 `packages/llm/llm-pi-ai`。
---
## 9. 能力分层(三个角色,可替换能力的设计模式)
当一个能力需要可替换的提供方(如 Bash 执行),拆成三个角色、放入不同包:
1. **Service Definition**(如 `dsh-shell`):定义 Cordis 服务接口 + Request/Result 类型。`abstract class MyCapService extends Service { super(ctx,'myCap'); abstract execute(req): Promise<res> }`
> **硬约束**:Definition **必须是一个 Cordis `Service`**(抽象类或具体注册表),**绝不能是 TypeScript `interface`**——接口在运行时不存在,无法参与服务解析。术语「seam」指 Definition + Provider + Consumer 这**三个角色的整体**,不是单指接口。
2. **Service Provider**(如 `dsh-bash-local`):实现接口。`class MyCapLocal extends MyCapService { ... }`;`apply(ctx){ ctx.plugin(MyCapLocal) }`
3. **Consumer**(如 `dsh-tool-bash`):把能力暴露为模型工具。`inject: ['tools','myCap']`
**依赖方向**:Provider 依赖 Definition;Consumer 依赖 Definition;Provider 与 Consumer **互不依赖**。更换提供方只改 `cordis.yml` 一行,Definition 与 Consumer 不动。
> **先看有没有现成的扩展点**:只想附加策略或适配器时**不要新增 seam**——优先挂已有的事件/钩子(见 §7 与 `References/11-cookbook.md`)。完整 seam 目录见 `References/08-capability-layering.md`。
> **不要预防性拆分**:只有角色需要独立演进才拆包。简单工具插件一个包就够。
---
## 10. 打包与安装(把插件交付给用户)
### 两个概念
- **组合包(bundle)**:附带一个配置层的 npm 包,manifest 声明 `dsh.bundle`。是你编写并分发的东西。
- **profile**:位于 `$DSH_HOME/profiles/<name>` 的可启动组合,manifest 声明 `dsh.profile`(只含有序 `bundles` 列表;**0.2.0 起 `patchReload` 字段已删除**,写了也不生效)。是用户启动的东西。
**不要手写 profile manifest**:`web` / `headless` / `sdk` / `sdk-minimal` / `acp` 首次使用时自动从随附模板初始化。要派生自定义 profile:
```sh
dsh --profile demo --from-default-profile web # 从随附模板派生(不复制依赖与 patch)
```
目标名不能是随附模板名,也不能用保留名 `desktop`。
### 打包一个组合包
```
hello-plugin/
├── package.json # 声明 dsh.bundle
├── cordis.patch.yml # 层内容
└── index.js # 插件模块
```
```jsonc
// package.json
{
"name": "dsh-hello-plugin",
"version": "0.1.0",
"type": "module",
"main": "index.js",
"files": ["index.js", "cordis.patch.yml"],
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } } // patch 也可给数组:按序作为同一层应用
}
```
```yaml
# cordis.patch.yml —— 与 --patch overlay 相同的格式;按包名引用
- insert:
- id: hello
name: dsh-hello-plugin
```
### 安装进 profile
```sh
dsh plugin --profile demo add ./hello-plugin # 或 github:you/hello-plugin,或 npm 包名
dsh plugin --profile demo add . # 相对 spec 按调用目录锚定 → 装当前 checkout
dsh --profile demo --dump-config # 先验证完整配置层(含 --patch)
dsh --profile demo --dump-default-config # 只看组合包各层(不能与 --patch 同用)
dsh --profile demo --dump-config-schema # 新增:输出组合条目与 patch 层的 JSON Schema(2020-12)
dsh demo # 简写:等价于 dsh --profile demo
dsh --profile demo # 再启动
dsh plugin --profile demo remove dsh-hello-plugin # 移除
```
- `add` 后 `dsh.profile.bundles` 会按依赖顺序**重算**;组合包成员变化后**需要重启 profile**;profile / `$DSH_HOME` 的 `cordis.patch.yml` 编辑在 base 系 profile(含 `web`)下**默认热重载**。
- **`dsh plugin` 有三个自管子命令**(不转发给 pnpm):`version-exemptions`、`allow-version <pkg@version> --dsh-version <exact> --accept-risk`、`revoke-version …`,豁免记在 profile 的 `compatibility.json`。`add`/`install` 具名包会**先做兼容检查**,被拒时会打印确切的 `allow-version` 命令。`--profile` 必填;没有 `build`/`pack`/`publish` 子命令。
- **加载失败不致命**:无法解析、不可读、没声明 `dsh.bundle`、或 DSH peer 不兼容且未豁免的 bundle 会被**跳过**,仍留在 `bundles` 列表里并进 `skippedBundles`,每次启动打印一次;其余 bundle 保持顺序。profile manifest 与用户 patch 的错误仍会让启动失败。
- 包只有在声明了 `dsh.bundle` 时才会作为组合包生效;`update` 后才获得声明的包会被自动激活。
### 配置层顺序(理解覆盖语义)
1. `dsh.profile.bundles` 中各组合包 patch,按列表顺序
2. profile 自己的 `cordis.patch.yml`
3. `$DSH_HOME/cordis.patch.yml`(机器级偏好)
4. 每个 `--patch <path>` overlay(按 argv 顺序)
**后应用者按行胜出,且 patch 会替换目标行整个 `config` 值(不是深合并)**。因此:覆盖别层某行时要重述该行需要的每一个键;优先给出用户大概率保留的默认值,其余交给 schema。省略的键回落到**插件 schema 的默认值**,而不是保留上一层写入的值。
⚠️ 副作用:如果你用 `!!js` 从运行时读值(如 `port: !!js ctx.webStartup.port ?? 8080`),用户用**字面量替换整个 `config`** 会把这次运行时读取一起抹掉。
> 另有 launcher 自行追加的一层(用户不可编写,仅供解释):当环境变量 `DSH_TELEMETRY_DISABLED` 为非空值且组合里存在该行时,追加 `{ id: 'session-telemetry-otel', disabled: true }`。
### 从 git 安装的两个坑
- git 安装拉源码不跑 `build`,**作者必须提供自包含的 `prepare` 脚本**。
- pnpm ≥10 默认拒绝 git 依赖的 `prepare`,**用户需在 profile 的 `pnpm-workspace.yaml` 加 `allowBuilds: <pkg>: true`** 并重新 `add`。要诚实告知用户:这等于允许该包在安装时执行代码。
- 不想让用户授权构建 → 发布 npm,或交付**已构建的** tarball(`pnpm pack`):tarball 与本地 checkout 安装都**不需要** `allowBuilds`。
### 组合包作者要注意的三件事(0.2.0)
1. **DSH peer 依赖契约**:导入前 DSH 会把 `peerDependencies` 中每个匹配 `@deepseek-ai/dsh` 或 `@deepseek-ai/dsh-*` 的条目,与**运行时那一个版本**比对;声明的每个范围都必须匹配,未声明 DSH peer 不构成约束,非法范围视为不兼容。不兼容的 bundle 会被拒绝或跳过,除非 profile 的 `compatibility.json` 里有精确 `name@version` 豁免。**`engines.dsh` 只是声明性的,不是被强制检查的字段**;linked checkout 场景建议把共享 dsh 包同时写进 `peerDependencies` 与 `devDependencies`。
2. **可选展示元数据**:`locale/<lang>.json` 里的 `meta.title` / `meta.description`(配合 `exports` 暴露 `./locale/*.json`),以及 `package.json` 顶层 `icon`(SVG/PNG/JPEG/WebP,≤256 KiB,必须在包内且真实路径不逃逸)。它们显示在 Plugin Manager 的卡片/详情上,**不会激活插件**;用 `pnpm run verify-package-meta` 校验。**导出子路径 bundle** 的展示元数据走另一条路:子路径不再读取独立的 `package.json`(自 0.2.0-rc.2 的 `subpath-plugin-display-manifest` 升级起),标题/描述走子路径的 `locale/*.json` 的 `meta.title`/`meta.description`,图标走子路径的 `icon` 导出。仓库内的升级指南页面:`docs/upgrade-guide/v0.2.0-rc.2/subpath-plugin-display-manifest/guide.md`。
3. **Plugin Manager 已随 0.2.0 交付**:`plugin_manager` 工具可 `install_bundle` / `set_bundle` / `set_plugin` / `remove_bundle` 并管理版本豁免,Web 也有「插件」页,改动**跨会话持久**;`dsh-tool-cordis` 现在**只能只读查看**(`cordis_inspect_list` / `cordis_inspect_query`),早期的 `cordis_define` / `cordis_run` 已移除。
---
## 11. 开发流程(Agent 照着走)
### 阶段 A:侦察(动手前必做)
1. 读目标仓库/项目结构:`cordis.yml`(或 profile 的 `cordis.patch.yml`)、`packages/*/package.json`、现有插件源码。
2. 确认要用的服务存在且知道其 API:查 `ctx.<name>` 类型定义或子系统文档(`References/08-capability-layering.md`)。
3. 确认要监听的事件名与分发模式(`tools/*`、`agent/*`、`session/event` 等)。
### 阶段 B:实现
1. 按第 3-8 节模板写插件:`name` / `inject` / `Config` / `apply(ctx, config)`。
2. 所有副作用走 `ctx.*` API 或 `ctx.effect()`。
3. 所有可调参数进 `Config`(带默认值),无硬编码。
4. 定义完整的 `Config` schema,使错误配置在加载时响亮失败。
5. 工具:定义清晰 `description` 与参数说明、规范的 `output.schema`、纯函数 `output.render`(和可选展示器)。
6. 处理 `exec.signal` 取消;长任务用 `ctx.jobs` 后台化。
### 阶段 C:验证
1. 类型检查与构建通过(如 `pnpm run typecheck && pnpm run build`,在 DSH 仓库内)。
2. 用 `--patch` 或 `dsh plugin add` 加载,用 `--dump-config` 确认配置层。
3. 启动运行,确认:插件日志出现、工具/服务/事件按预期工作。
4. 测试卸载/重载:改**配置**确认注册被清理、无泄漏、无残留监听(base 系 profile 默认热重载配置);**源码模块热替换默认关闭**,要用 profile patch 打开 `hmr` 的 `root`,否则重启进程验证。
5. 测试依赖缺失场景:去掉提供方 → 插件 PENDING 不崩溃;恢复 → 自动加载。
6. 测试错误配置:传非法 config → 明确报错。
### 阶段 D:交付
1. 按第 10 节打包(`dsh.bundle` + patch + 自包含 prepare 脚本,或发布 npm/tarball)。
2. 写包 README:服务 API / 配置 / 事件 / 扩展点 / 设计说明。
3. 遵循仓库测试策略补测试与组装覆盖。
### 覆盖范围边界
本技能**专注于**:plugin 模型心智模型、`tool` / `llm` / `service`(含 provider + consumer)/ `event` / `bundle`(含 profile + manifest + Plugin Manager)——这五类能力足以覆盖绝大多数 Agent 插件需求。
本技能**不覆盖**(需要直接查官方仓库的子系统页面):
- **Host plugin / UI 扩展**(Web 客户端组件、composer 输入区、统计面板装饰器等):0.2.1 把 `composer-stats` 扩展点拆为 `activity` 与 `usage` 两个独立 ID,覆盖旧 `stats` 整行的插件要更新注册 ID。详见 `docs/subsystems/` 下对应文档(仅仓库内,未发布到文档站)。
- **Schedule / 自动化任务扩展**:0.2.1 起自动化任务改为 Web 内置能力,`schedule_*` 工具按模式提供(标准、创造、PTC 模式可用;极简模式与子代理不可用),并新增 `subagent_session` 错误码。子系统页面:https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/schedule 。
- **MCP / SSH / browser-use / computer-use / otel / product-telemetry / boot / voice-input / office-to-pdf** 等 0.2.0 新增的子系统,文档均只存在于上游仓库 `docs/subsystems/`,相关文件内已给链接。
- **Claude Code Mods 兼容层**(0.2.1-alpha.1 引入的实验性 bridge):仅仓库 `docs/subsystems/claude-code-mods.md`,文档站尚未发布。
---
## 12. 常见错误与修正(对照自查)
| 错误 | 修正 |
|---|---|
| 手动 `removeListener` / `clearInterval` | 全部改为 `ctx.on` / `ctx.effect`,卸载自动清理 |
| `export const Config = { ... }`(普通对象) | 用 `Schema.object({...})` 构建 Standard Schema |
| 硬编码 timeout/路径/阈值 | 移入 `Config` 字段并给默认值 |
| 在 `output.render` / `presentCall` 里做 I/O、读文件、用时钟 | 保持纯函数;I/O 进 `execute`,持久元数据进 `presentationMeta` |
| 工具 `execute` 返回人类可读文本而不是规范值 | 返回 `output.schema` 声明的规范 JSON 值;文本归 `output.render` |
| waterfall 监听器忘调 `next()` | 观察者必须 `next()`;不调 = 有意短路 |
| 用 `ctx.get()` 探测必需依赖 | 必需依赖写 `inject` |
| 服务名撞车(`ctx.tools`、`ctx.llm` 等) | 自有服务加辨识度前缀 |
| patch 覆盖别层行只写改动的键 | 重述该行全部所需键(整 config 替换语义) |
| 未声明 `inject` 却直接用 `ctx.xxx` | 补 `inject`(或改用 `ctx.get` 做可选探测) |
| 仍在用 `ctx.codeRuntime` / `dsh-code-runtime` | 0.2.0 已改名:`ctx.ptcRuntime` / `dsh-ptc-runtime`(提供方 `dsh-ptc-runtime-node`) |
| 在 profile manifest 里写 `patchReload` | 该字段已删除;HMR 由 YAML 组合里 `dsh-hmr` 行的 `disabled`/`root` 决定 |
| 后台任务写 `owner: exec.agent` 或 `readOutput` | `owner` 要传 `exec.agent.id`;`readOutput` 已移除,改用 `spec.output` / `job.append()`,结果用 `JobOutcome.result` |
| 导入 `@deepseek-ai/dsh-invariants` 或 `<pkg>/invariant` | 0.2.1-alpha.1 已删除:`InvariantRegistry`、`InvariantInstaller`、`InvariantFailure`、`InvariantError` 不再导出;`sdk-minimal` 中的五个 `*-invariant` id 已清理。诊断失败改用自己的通道上报 |
| waterfall 调用方忘传最后一个 `next` 实参 | 调用方传最内层延续:`ctx.waterfall(name, ...args, next)`(与「监听器必须调 `next()`」是两件事) |
| 怀疑 SKILL.md 与官方 DSH 不一致 | 跑 `bash scripts/verify.sh`:版本三处一致 + frontmatter 合法 + 官方链接 200(CI 也会跑) |
---
## 13. 参考资料索引(本项目 `References/` 目录)
| 文件 | 内容 |
|---|---|
| `References/01-dsh-architecture.md` | DSH 架构总览:插件模型、CLI、profile/bundle、配置层 |
| `References/02-plugin-basics.md` | 第一个插件、三种形态、inject、effect、生命周期 |
| `References/03-tools.md` | 工具开发完整参考:defineTool、执行流水线、后台任务、UI 卡片 |
| `References/04-config.md` | 插件配置与 Schemastery |
| `References/05-llm-adapter.md` | LLM 适配器完整指南 |
| `References/06-framework-services-events.md` | 服务与依赖、事件系统、生命周期细节 |
| `References/07-publish.md` | 打包、安装、profile、配置层顺序 |
| `References/08-capability-layering.md` | 三种角色能力设计与 seam 目录 |
| `References/09-cordis-primer.md` | Cordis 入门:五个核心概念、分发模式、ctx API |
| `References/10-spatiotemporal.md` | 论文《A Programming Paradigm for Spatiotemporal Composability》解读([arXiv:2608.25512](https://arxiv.org/abs/2608.25512)) |
| `References/11-cookbook.md` | 扩展模式:权限门禁、UI 插件、协议桥、功能→机制映射表 |
官方文档入口:
- 开发者第一步:https://deepseek-harness.github.io/deepseek-harness/develop/basic/ (中文)/ `/en/develop/basic/`(英文)
- 服务与 seam 全局视图:https://deepseek-harness.github.io/deepseek-harness/reference/capability-seams
- 0.2.0-rc.2 升级指南(含 `remove-runtime-invariants`、`subpath-plugin-display-manifest`、`schedule-bundle-retired`、`account-sign-in-errors`):仓库内 `docs/upgrade-guide/v0.2.0-rc.2/`(文档站尚未发布索引页)。
- PTC 运行时子系统页面(自 0.2.1-alpha.1 起从 `code-runtime` 改名):https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/ptc-runtime
- Claude Code Mods 兼容层(0.2.1-alpha.1 新增的实验性 bridge):仓库内 `docs/subsystems/claude-code-mods.md`(文档站尚未发布)。
源码仓库:https://github.com/deepseek-ai/deepseek-harness
本技能自身:仓库 https://github.com/green-dalii/dsh-plugin-dev-skill ,版本见同目录 `VERSION` 文件(更新检查流程见 §0.1)。
> 索引与**术语对照表**(上游重命名备忘,如 Code Mode → PTC mode、`CallId` → `ToolCallId`、`ctx.codeRuntime` → `ctx.ptcRuntime`)见 `References/00-INDEX.md`;注意文档站发布的是 `develop/**`、`guide/**`、`reference/**` 与已发布的部分 `reference/subsystems/*`(已确认的有 `ptc-runtime`、`schedule`、`approval` 等),而 `glossary`、`event-producer-consumer`、`defensive-patterns` 以及多数 `subsystems/**` 页面(`deliverables`、`mcp`、`ssh`、`browser-use`、`computer-use`、`otel`、`product-telemetry`、`boot`、`voice-input`、`office-to-pdf` 以及 0.2.1 新增的 `claude-code-mods` 等)**仅存在于仓库**,相关文件内已给出 GitHub 链接。
Files in this skill
- CHANGELOG.md
- CONTRIBUTING.md
- README.zh-CN.md
- References/00-INDEX.md
- References/01-dsh-architecture.md
- References/02-plugin-basics.md
- References/03-tools.md
- References/04-config.md
- References/05-llm-adapter.md
- References/06-framework-services-events.md
- References/07-publish.md
- References/08-capability-layering.md
- References/09-cordis-primer.md
- References/10-spatiotemporal.md
- References/11-cookbook.md
- SECURITY.md
- SKILL.md
- VERSION
- llms.txt
Attribution
Comments
Loading comments…