Skip to content
源码版本47f9438 (dsh@0.1)

依赖解析与顺序

Cordis 的"加载顺序"不是拓扑排序,而是 service-availability 驱动的反应式系统——fiber 在 inject 声明的服务全部 active 时才 LOADING→ACTIVE,否则永远 PENDING(无报错,因为 provider 可能后到)。

不是拓扑排序

这是理解 Cordis 插件加载的关键认知转换:没有"先加载 A 再加载 B"的显式顺序。声明 inject: ['tools', 'systemPrompt'] 的插件会一直 PENDING,直到两个 service 都 active;若 tools 自身又 inject: ['systemPrompt'],Cordis 自动传递解析。

_checkImpl:服务是否可注入

vendor/cordis/src/fiber.ts:597-609_checkImpl(name):
ts
_checkImpl(name) {
  // 查 ctx.reflect._getImpl(name, true)
  // 若 impl 存在且 check() 通过则写入 _store
  // 否则删除
}

这是"服务是否可注入"的判定。

_refresh:epoch 字符串

vendor/cordis/src/fiber.ts:611-623_refresh():
ts
_refresh() {
  // 遍历 Object.keys(this.inject)
  // 任一 _store[name] 缺失就把 epoch 设为 INACTIVE (PENDING)
  // 否则拼 ':uid:uid' 字符串作为新 epoch
}
vendor/cordis/src/fiber.ts:625-639_setEpoch(epoch):epoch 变化即 _updateState——
  • 从 INACTIVE→非 INACTIVE 触发 _reload()(LOADING)
  • 反向触发 _unload()(UNLOADING)

为什么用 uid 拼接而非布尔

_setEpoch 用 uid 拼接而非布尔:provider 被 reload 时 uid 变,所有下游 fiber 的 epoch 字符串变,自动级联 reload——这是 HMR 无需手动簿记的根因

inject 的两种写法

plugin() 内部解析 inject

vendor/cordis/src/registry.ts:316-336plugin(plugin, config):
ts
plugin(plugin, config) {
  // 新建 runtime → new Fiber(ctx, config, Inject.resolve(plugin.inject), runtime, …)
  // inject 在 fiber 构造时就被解析进 ctx.intercept 覆盖
}

inject() 语法糖

vendor/cordis/src/registry.ts:300-302inject(inject, callback) 其实是 plugin({ inject, apply: callback, name: callback.name }) 的语法糖:callback 在服务可用时执行、服务消失时卸载并重跑。

动态插件的启动:guardedPlugin

startHostHalf

packages/extensions/cordis-host-runner/src/lifecycle.ts:22-45startHostHalf(group, plugin, reportGuardFailure):
ts
async function startHostHalf(group, plugin, reportGuardFailure) {
  await group.await()                              // 等组就绪
  const fiber = group.ctx.plugin(guardedPlugin(plugin))  // 包装后挂载
  await fiber.await()
  // 失败即 fiber.dispose() 不留 FAILED fiber 残留
}

group.await() 确保动态插件所挂的 cordis-dynamic 组 fiber 就绪后再起子 fiber,避免在组未 ACTIVE 时 mount 导致孤儿 fiber。

guardedPlugin 的沙箱

guardedPlugin 包装:动态插件的 apply 收到的是 sandboxContext(ctx, reportFailure) 而非真实 ctx,只暴露白名单 verb(ctx.get/ctx.on/ctx.provide/ctx.effect),框架内部与 context-valued 返回被拒。

Sandbox 不是安全边界:host-sandbox.ts 注释明说 "is not containment: host-realm helper functions remain an escape route"。动态插件用于 cooperative package(可观察可处置),不是防恶意代码。

诊断:为什么我的插件不加载

packages/extensions/cordis-host-runner/src/lifecycle.ts:55-57missingServices(ctx, fiber):
ts
function missingServices(ctx, fiber) {
  // 用 ctx.get(service) === undefined 列出 fiber 还在等的 service
}

这是"为什么我的插件不加载"的诊断答案。cordis_inspect_self 工具的"settled-but-pending"报告就来自此函数,告诉 agent:"你的插件在等 fs service"。

反应式模型总结

整个系统没有"调度器"在排顺序,只有每个 fiber 各自观察自己关心的服务,服务可用性变化时自动反应。

接下来读什么