DSH plugin 事件怎么写?ctx.on 与 ctx.emit 基础、五种分发模式与真实 Harness 事件

插件开发发布于 2026-10-02作者: DeepSeek Plugin 插件市场
DSH pluginDeepSeek Harness事件系统ctx.onCordis
DeepSeek Harness 的 DSH plugin 事件:ctx.on 监听、ctx.emit 触发,掌握五种分发模式(waterfall 必须调用 next()),并识别 tools/result、agent/request、approval/request 等真实事件,监听器一律写在 apply 内。

DSH plugin 的事件系统就三件事:用 ctx.on / ctx.emit 做声明、触发、监听,从五种分发模式里选对一种,再把监听器挂到真实的 Harness 事件上——并且一律写在 apply 里。 它是 Cordis 插件间通信的核心机制,Harness 大量用它做松耦合扩展点;五种分发模式各有契约,选错会导致返回值丢失或顺序错乱。无论你叫它 DSH插件 还是 DeepSeek插件,事件写法完全一致。

DSH plugin 事件怎么写:ctx.on 与 ctx.emit 基础用法

一个 DSH plugin 事件由「类型声明 + 触发方 + 监听方」三部分组成,声明合并让 ctx.emit 与 ctx.on 都获得完整类型。 官方给出的基本用法就是两行(来源):

ts
ctx.on('event-name', (payload) => {
  // Handle the event.
})

ctx.emit('event-name', payload)

用声明合并给事件加类型

Harness 用 TypeScript 声明合并为事件提供类型安全,声明一次即可让触发与监听都正确推导。 在插件里扩展 Events 接口:

ts
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 的五种事件分发模式怎么选

事件采用哪种模式是它的约定的一部分,决定了监听器能否返回值、能否并发、能否彼此短路。 官方列出五种模式(来源):

模式调用语义
emitctx.emit(name, ...args)同步广播;不等待、不收集返回值
parallelawait ctx.parallel(name, ...args)所有监听器并发运行并一同等待
serialawait ctx.serial(name, ...args)按顺序运行并等待;第一个非 null/false/undefined 返回值胜出并停止后续
bailctx.bail(name, ...args)serial 的同步版本
waterfallctx.waterfall(name, ...args, next)环绕中间件,可转换或短路

判断口诀:只是通知别人 → emit;要拿第一个有效结果 → bail / serial;要包住下游返回值或拦截 → waterfall。

waterfall 必须调用 next(),否则短路整条链

waterfall 的监听器必须调用 next() 才会继续下传,不调用就是有意短路——这是官方刻意设计的行为,用来实现拦截 / 网关逻辑。 下面是官方演示(来源):

ts
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,随插件卸载自动移除。 官方日志插件是最小范例,监听工具调用与结果(来源):

ts
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.on 和 ctx.emit 分别负责什么?

DSH plugin 写事件就三步:先用声明合并声明事件签名,再用 ctx.emit 触发、用 ctx.on 监听。ctx.on 注册监听器,ctx.emit 同步广播事件给所有监听器;两者靠同一份类型声明获得提示,事件名遵循 namespace/action 命名。

DSH plugin 的 ctx.emit、ctx.bail、ctx.serial 有什么区别?

DSH plugin 事件共五种分发模式:ctx.emit 同步广播、不收集返回值;ctx.parallel 并发运行并等待;ctx.serial 按顺序执行并等待;ctx.bail 是 serial 的同步版本;ctx.waterfall 是环绕中间件。选错模式会导致返回值丢失或顺序错乱。

DSH plugin 的 waterfall 事件不调用 next() 会怎样?

DSH plugin 的 waterfall 监听器不调用 next() 会短路整条流水线。只做观察或标注的监听器必须调用 next() 才能把结果传给下游;有意拦截时才不调用直接返回,这是官方刻意设计的行为。

DSH plugin 的事件监听器需要手动 removeListener 吗?

不需要,DSH plugin 通过 ctx.on() 注册的监听器属于 effect,会在插件卸载时自动移除。所以监听一律写在 apply 里即可,不要手写清理逻辑,也不要在 apply 之外注册监听。

DSH plugin 怎么监听工具结果和 session/event 会话事件?

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

来源