$dshubuser@dshub:~/plugins$
  • 插件
  • 提交插件
  • 博客
inject:让依赖决定加载顺序,而不是 YAML 行号
2026/08/23

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

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

核心规则:需要服务才能运行的插件声明 inject;Cordis 让它等待服务就绪,而不是让开发者手写启动顺序。

这条规则把“谁先启动”的问题从配置文件的排列方式,改成了运行时可以观察和维护的依赖关系。

01 / 两行配置,顺序可以交换

Cordis dependency graph showing provider readiness before consumer activation

提供方:

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

export class GreeterService extends Service {
  constructor(ctx: Context) {
    super(ctx, 'greeter')
  }
}

消费方:

export const inject = ['greeter']

export function apply(ctx: Context) {
  console.log(ctx.greeter.greet('world'))
}

两项配置可以按任意顺序写入 cordis.yml。Consumer 的 apply 只有在 greeter 已注册后才运行。

数组形式只声明服务名,Cordis 会把它归一化为“服务名 → null 配置”。如果服务支持局部 intercept 配置,inject 也可以使用对象形式,把服务名映射到对应配置。

02 / PENDING 是等待,不是半启动

Cordis injected plugin state flow from PENDING to LOADING and ACTIVE or back to PENDING

当依赖不存在时,Fiber 停留在 PENDING:

  • apply 不会被调用,因此插件不会运行一半。
  • 进程不会因为一个等待中的 Fiber 自动保持事件循环活跃。
  • provider 稍后挂载时,依赖满足,consumer 才会进入 LOADING;只有 config 和 apply 都成功,才会到达 ACTIVE。

PENDING 不等于 FAILED:缺少依赖会等待,config 或 apply 抛错才会失败。这也是“没有输出”最常见的解释之一。先检查服务名和 provider 是否进入了当前组合,再检查插件代码。

03 / 依赖会在运行中继续被跟踪

inject 不是只在启动瞬间做一次的检查。如果 provider 被卸载或热替换,依赖它的 consumer 会一起卸载;provider 恢复后,consumer 会重新加载。

Cordis unloading a provider and its consumers, then reloading them when the service returns

这和 effect 的所有权一起工作:consumer 的监听器、注册和外部资源在依赖消失时先撤销,避免它继续引用不可用的服务。

因此可以把 shell 的本地 provider 换成 E2B provider;所有 inject: ['shell'] 的插件不需要改代码,就会使用新实现。

04 / 硬依赖和可选依赖

硬依赖适合“没有它就没有意义”的插件:

export const inject = ['tools']

可选依赖适合“有它更好,没有也能工作”的插件:

export function apply(ctx: Context) {
  const greeter = ctx.get('greeter')
  console.log(greeter ? greeter.greet('maybe') : 'no greeter available')
}

不要把可选服务放进 inject,否则缺少 provider 时整个插件会合法地停在 PENDING。ctx.get() 是运行时探测,不会创建依赖边;provider 后来出现时,插件也不会因为这次探测自动重新运行 apply。

05 / 依赖关系比启动脚本更稳定

手写启动脚本需要维护拓扑排序、重试和卸载顺序;inject 把这些变化交给 Fiber 和注册表。增加一个 provider、替换一个 provider,或让一个服务短暂离场,都沿用同一套就绪语义。

在 DSH 中,工具、模型、会话和 Agent 能力因此可以被 profile 重新组合,而不需要让每个消费者知道完整的启动图。

下一步

  • 服务与上下文:共享能力
  • 生命周期与 effect:可逆的注册
  • 组合与热重载(HMR)

来源:服务教程、生命周期教程、Fiber 实现。

全部文章

作者

avatar for DSH Research
DSH Research

分类

  • Agent 基础设施
01 / 两行配置,顺序可以交换02 / PENDING 是等待,不是半启动03 / 依赖会在运行中继续被跟踪04 / 硬依赖和可选依赖05 / 依赖关系比启动脚本更稳定下一步

更多文章

waterfall:用 next() 做拦截、包装与短路
Agent 基础设施

waterfall:用 next() 做拦截、包装与短路

沿着一条真实的 Cordis waterfall 链路,理解监听器如何委托下游、包装结果,或在拥有决策权时直接短路。

avatar for DSH Research
DSH Research
2026/08/26
cordis.yml:把插件列表变成可修改的应用组合
Agent 基础设施

cordis.yml:把插件列表变成可修改的应用组合

理解 Cordis 配置项、稳定 id、group、isolate 与 patch 层,读懂配置文件如何定义一次运行。

avatar for DSH Research
DSH Research
2026/08/21
组合与热重载(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