DSH plugin 事件怎么写?ctx.on 与 ctx.emit 基础、五种分发模式与真实 Harness 事件
DSH plugin 的事件系统就三件事:用 ctx.on / ctx.emit 做声明、触发、监听,从五种分发模式里选对一种,再把监听器挂到真实的 Harness 事件上——并且一律写在 apply 里。 它是 Cordis 插件间通信的核心机制,Harness 大量用它做松耦合扩展点;五种分发模式各有契约,选错会导致返回值丢失或顺序错乱。无论你叫它 DSH插件 还是 DeepSeek插件,事件写法完全一致。
DSH plugin 事件怎么写:ctx.on 与 ctx.emit 基础用法
一个 DSH plugin 事件由「类型声明 + 触发方 + 监听方」三部分组成,声明合并让 ctx.emit 与 ctx.on 都获得完整类型。 官方给出的基本用法就是两行(来源):
ctx.on('event-name', (payload) => {
// Handle the event.
})
ctx.emit('event-name', payload)
用声明合并给事件加类型
Harness 用 TypeScript 声明合并为事件提供类型安全,声明一次即可让触发与监听都正确推导。 在插件里扩展 Events 接口:
import '@deepseek-ai/cordis'
declare module '@deepseek-ai/cordis' {
interface Events {
'my-plugin/ready': (payload: { id: string }) => void
'my-plugin/check': (input: string) => boolean | undefined
'my-plugin/transform': (input: string, next: () => Promise<string>) => Promise<string>
}
}
命名遵循 namespace/action 约定,扁平的事件命名空间才保持易读,例如 agent/pre-step、agent/request、tools/result、session/event(来源)。
DSH plugin 的五种事件分发模式怎么选
事件采用哪种模式是它的约定的一部分,决定了监听器能否返回值、能否并发、能否彼此短路。 官方列出五种模式(来源):
| 模式 | 调用 | 语义 |
|---|---|---|
| emit | ctx.emit(name, ...args) | 同步广播;不等待、不收集返回值 |
| parallel | await ctx.parallel(name, ...args) | 所有监听器并发运行并一同等待 |
| serial | await ctx.serial(name, ...args) | 按顺序运行并等待;第一个非 null/false/undefined 返回值胜出并停止后续 |
| bail | ctx.bail(name, ...args) | serial 的同步版本 |
| waterfall | ctx.waterfall(name, ...args, next) | 环绕中间件,可转换或短路 |
判断口诀:只是通知别人 → emit;要拿第一个有效结果 → bail / serial;要包住下游返回值或拦截 → waterfall。
waterfall 必须调用 next(),否则短路整条链
waterfall 的监听器必须调用 next() 才会继续下传,不调用就是有意短路——这是官方刻意设计的行为,用来实现拦截 / 网关逻辑。 下面是官方演示(来源):
ctx.on('demo/transform', async (input, next) => {
const downstream = await next()
return downstream.toUpperCase()
})
ctx.on('demo/transform', async (input, next) => {
if (input.includes('blocked')) return '** blocked **'
return next()
})
由此得到一条纪律:只负责观察或标注的 waterfall 监听器必须调用 next()。 忘记调用会悄无声息地吞掉所有下游的默认行为;Harness 就用 waterfall 处理协作插件可包装或作答的决策,例如 agent/request 替换模型调用配置、approval/request 由策略代替用户作答。
DSH plugin 真实事件:工具结果、模型请求与审批怎么监听
真实 Harness 事件就是 tools/result、agent/request、approval/request 这类能直接监听的事件,而它们的监听器一律写在 apply 里——通过 ctx.on() 注册的监听器是 effect,随插件卸载自动移除。 官方日志插件是最小范例,监听工具调用与结果(来源):
import type { Context } from '@deepseek-ai/cordis'
import '@deepseek-ai/dsh-tools'
export const name = 'tool-logger'
export function apply(ctx: Context) {
ctx.on('tools/result', (exec, result) => {
console.log(`[tool] ${exec.name}(${JSON.stringify(exec.arguments)})`)
const text = result.content
.map(block => block.type === 'text' ? block.text : '')
.join('')
console.log(`[tool result] ${text.slice(0, 100)}`)
})
}
监听器是 effect,卸载即消失。 通过 ctx.on() 注册的监听器会在插件卸载时自动移除,绝对不要手写 removeListener(来源)。这一点与 开发规范 里「所有能力通过 ctx 注册」是同一条主线。
分清 Cordis 事件与持久化会话事件
turn/*、step/*、tool/call、tool/result、compaction/* 是持久化会话事件类型,不是同名的 Cordis 事件。 想观察它们要监听 session/event 再检查 event.type;而 tools/result、agent/request 这类才是可直接监听的事件。
写完自检三项:监听是否都写在 apply 内;事件是否符合 namespace/action 命名;waterfall 监听器是否明确决定「调用 next() 还是有意短路」。想把手里的扩展点做成可被别人替换的实现,接着看 能力的三角色设计;想开发完发布,走 打包成 bundle,并在 DSH Plugin Hub 对照社区插件的事件用法。
常见问题
DSH plugin 写事件就三步:先用声明合并声明事件签名,再用 ctx.emit 触发、用 ctx.on 监听。ctx.on 注册监听器,ctx.emit 同步广播事件给所有监听器;两者靠同一份类型声明获得提示,事件名遵循 namespace/action 命名。
DSH plugin 事件共五种分发模式:ctx.emit 同步广播、不收集返回值;ctx.parallel 并发运行并等待;ctx.serial 按顺序执行并等待;ctx.bail 是 serial 的同步版本;ctx.waterfall 是环绕中间件。选错模式会导致返回值丢失或顺序错乱。
DSH plugin 的 waterfall 监听器不调用 next() 会短路整条流水线。只做观察或标注的监听器必须调用 next() 才能把结果传给下游;有意拦截时才不调用直接返回,这是官方刻意设计的行为。
不需要,DSH plugin 通过 ctx.on() 注册的监听器属于 effect,会在插件卸载时自动移除。所以监听一律写在 apply 里即可,不要手写清理逻辑,也不要在 apply 之外注册监听。
DSH plugin 监听工具结果用 ctx.on('tools/result', handler);想观察 turn、step、tool/call 这类持久化会话事件,则监听 session/event 再检查 event.type。因为这两类是持久化会话事件类型,不是同名的 Cordis 事件。
相关术语
- ctx.on
- ctx.on 是 DSH plugin 注册事件监听器的入口,注册的监听器属于 effect,会随插件卸载自动移除,无需手动 removeListener。— https://deepseek-harness.github.io/deepseek-harness/develop/framework/events
- waterfall
- waterfall 是 DSH plugin 的五种事件模式之一,属于环绕中间件:监听器拿到参数与 next() 延续,可包装下游返回值,也可不调用 next() 直接返回以短路整条链。— https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial/04-events
- ctx.bail
- ctx.bail 是 serial 的同步版本,监听器按顺序运行,第一个不是 null、false 或 undefined 的返回值成为最终结果并终止后续监听器。— https://deepseek-harness.github.io/deepseek-harness/develop/cordis-tutorial/04-events
- session/event
- session/event 是 DSH plugin 里用于观察持久化会话事件的监听点,turn/*、step/*、tool/call 等是持久化会话事件类型而非同名 Cordis 事件,需监听 session/event 并检查 event.type。— https://deepseek-harness.github.io/deepseek-harness/develop/framework/events
来源
- DeepSeek Harness 官方文档 - 事件系统· deepseek-ai
- DeepSeek Harness 官方文档 - Cordis 教程:事件· deepseek-ai