$dshubuser@dshub:~/plugins$
  • 插件
  • 提交插件
  • 博客
生命周期与 effect:让 Cordis 注册可以回卷
2026/08/24

生命周期与 effect:让 Cordis 注册可以回卷

跟随 Fiber 从 PENDING 到 DISPOSED,理解 effect、disposer 与 HMR 如何共同管理插件副作用。

核心结论:插件加载时创建资源,卸载时执行 disposer;Fiber 把两件事绑定在同一个生命周期里。

所以 HMR 不是“再 import 一次文件”这么简单。实现可以先导入替换模块,再卸载旧 Fiber;关键不变量是旧 Fiber 的监听器、服务、子插件和外部资源必须在新 Fiber 激活前撤销干净。

01 / Fiber 是插件实例的运行时句柄

Cordis Fiber state machine from PENDING through ACTIVE and UNLOADING to DISPOSED

一个插件实例会经历:

PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
                 ↘ FAILED
  • PENDING:插件已声明,但依赖尚未满足。
  • LOADING / ACTIVE:apply 正在运行或已经完成。
  • FAILED:配置校验或 apply 抛出异常。
  • UNLOADING / DISPOSED:清理正在执行或已经完成。

Fiber 不是一个抽象状态标签。它是插件实例拥有的生命周期句柄,fiber.dispose() 会等待清理完成,并递归卸载它挂载的子插件。

02 / 用 effect 包住外部资源

Cordis effect ownership connecting plugin load resources to unload disposers

Cordis 已经知道如何撤销自己的注册;定时器、连接和 watcher 等外部资源则需要显式包装:

ctx.effect(() => {
  const timer = setInterval(() => console.log('tick'), 200)
  return () => {
    clearInterval(timer)
    console.log('cleaned up')
  }
})

主体在加载时执行,返回的函数在卸载时执行。插件作者不需要在某个全局 shutdown 函数里猜测资源属于谁。

03 / 常见注册本身也是 effect

下列操作会把清理关系附着到当前 Fiber:

  • ctx.on() 注册事件监听器。
  • ctx.plugin() 挂载子插件。
  • Service 注册服务名称。
  • ctx.effect() 包装 Cordis 尚未管理的外部资源。
Cordis Fiber effect stack showing reverse cleanup for listeners services children and timers

卸载时,Fiber 按注册的逆序收集 disposer,再并发启动顶层清理;多个并列 effect 的异步清理可以并发运行,而 fiber.dispose() 会等待它们完成。如果两个资源之间有严格顺序要求,把清理放在同一个 effect 中,并在其中显式 await。

04 / 一个可验证的最小例子

import type { Context } from '@deepseek-ai/cordis'

function heartbeat(ctx: Context) {
  console.log('heartbeat plugin loading')
  ctx.effect(() => {
    const timer = setInterval(() => console.log('tick'), 200)
    return () => {
      clearInterval(timer)
      console.log('heartbeat cleaned up')
    }
  })
}

export function apply(ctx: Context) {
  const fiber = ctx.plugin(heartbeat)
  ctx.effect(() => {
    const shutdown = setTimeout(async () => {
      await fiber.dispose()
      console.log('disposed')
      process.exit(0)
    }, 700)
    return () => clearTimeout(shutdown)
  })
}

输出会先出现 heartbeat 和 tick,随后出现 cleanup 和 disposed。重要的不是日志,而是 fiber.dispose() 返回前清理已经完成。

05 / effect 是 HMR 的基础设施

HMR 可以把代码刷新变成可控的运行时转换:先导入替换模块,再卸载旧 Fiber 并回卷 effect,随后注册并激活替换 Fiber。没有 disposer 的资源不会神奇消失,因此每个插件都应该让自己创建的资源有明确 owner。

在 DeepSeek Harness 中,工具注册、事件监听、模型适配器和 agent loop 都通过插件进入这套生命周期。卸载不是文档里的约定,而是运行时动作。

下一步

  • 组合与热重载(HMR)
  • 事件:不知道谁在听,也能发通知
  • Cordis 在 DeepSeek Harness 里

来源:生命周期教程、Fiber 实现、Context API。

全部文章

作者

avatar for DSH Research
DSH Research

分类

  • Agent 基础设施
01 / Fiber 是插件实例的运行时句柄02 / 用 effect 包住外部资源03 / 常见注册本身也是 effect04 / 一个可验证的最小例子05 / effect 是 HMR 的基础设施下一步

更多文章

inject:让依赖决定加载顺序,而不是 YAML 行号
Agent 基础设施

inject:让依赖决定加载顺序,而不是 YAML 行号

理解 Cordis 的 PENDING、依赖跟踪和可选服务探测,解释为什么 provider 更换会自动带动 consumer。

avatar for DSH Research
DSH Research
2026/08/23
Cordis:把运行中的应用变成一棵可逆的插件树
Agent 基础设施

Cordis:把运行中的应用变成一棵可逆的插件树

从 cordis.yml、Loader 和 Context 开始,理解 Cordis 如何让 DeepSeek Harness 组合、替换并安全卸载运行时能力。

avatar for DSH Research
DSH Research
2026/08/18
组合与热重载(HMR):导入新模块,回卷旧 Fiber,再激活替换
Agent 基础设施

组合与热重载(HMR):导入新模块,回卷旧 Fiber,再激活替换

理解稳定 id、配置组和 Cordis HMR 的卸载—重载流程,并学会定位始终停在 PENDING 的插件。

avatar for DSH Research
DSH Research
2026/08/28

邮件列表

加入我们的社区

订阅邮件列表,及时获取最新消息和更新

[dshub] [zsh]$ dshub ls$ dshub submit$ dshub blog
-- NORMAL -- ✓ 2026 dshub