DeepSeek Harness 不从 npm 拉取 Cordis 插件框架。相反,vendor/ 存放 Cordis 及其基础库的源码拼接副本,改名为 @deepseek-ai 作用域并作为 workspace 包钉住。这让 harness 完全拥有它的框架层——可审计、可打补丁、可发布——并在构建 agent harness 时针对真实遇到的缺陷加以加固。
为何拼接
vendor/README.md 直白地陈述契约:这些副本之所以放在这里而不是经 npm 依赖,是为了让 harness 完全拥有其框架层。随之而来的三个推论:
- 改名为
@deepseek-ai作用域——cordis → @deepseek-ai/cordis、@cordisjs/plugin-* → @deepseek-ai/cordis-plugin-*。每个 harness 包都把cordis声明为 peer 依赖,因此发布 harness 也就发布了框架层;若按上游名发布则在注册表上侵权。 pnpm-workspace.yaml#linkWorkspacePackages: true使保留下来的 semver 范围解析到这些钉住的 workspace,包括从构建出的lib/导入。两个包另经overrides强制链接:@deepseek-ai/cosmokit与@deepseek-ai/schemastery→link:vendor/…。
卫生门禁 verify-vendored-links 断言每个拼接名的 pnpm-lock.yaml 都解析为 workspace 的 link:,且旁边没有注册表副本。rescope-vendor.ts 在一次上游同步后重新执行 @deepseek-ai 改名。
拼接包名册
| 目录 | npm 名(vendor/*/package.json) | 上游原名 | 在 dsh 中的作用 |
|---|---|---|---|
cordis/ | @deepseek-ai/cordis | cordis | 核心 DI/插件元框架——Context、Service、Fiber、生命周期、注入 |
cosmokit/ | @deepseek-ai/cosmokit | cosmokit | 通用工具(random、path、promise 辅助),跨族使用 |
schemastery/ | @deepseek-ai/schemastery | schemastery | 类型驱动 schema 校验器(z.object,…),用于每个插件 Config |
loader/ | @deepseek-ai/cordis-plugin-loader | @cordisjs/plugin-loader | 运行时插件 loader,持有 EntryTree;导入插件并应用配置 |
include/ | @deepseek-ai/cordis-plugin-include | @cordisjs/plugin-include | 文件支撑的 loader 树:读 YAML/JSON 配置为条目,写回更新 |
group/ | @deepseek-ai/cordis-plugin-group | @cordisjs/plugin-group | 嵌套插件组 / 条目嵌套 |
hmr/ | @deepseek-ai/cordis-plugin-hmr | @cordisjs/plugin-hmr | 热重载:监视源码、清模块缓存、只重载受影响的插件条目 |
logger-console/ | @deepseek-ai/cordis-plugin-logger-console | @cordisjs/plugin-logger-console | 内置 logger 服务的控制台导出器 |
timer/ | @deepseek-ai/cordis-plugin-timer | @cordisjs/plugin-timer | 可 dispose 的定时器服务(挂载于 base/spine 组合;schedule/提醒功能不用它——dsh-schedule 运行自己的 setTimeout 循环) |
每个库在代码中的作用
cordis——地基。一切皆插件:agent、LLM 层、工具与会话运行时都是 CordisService,登记为ctx.*。vendor/cordis/src/fiber.ts携带最重要的本地加固(见下)。站点的 Cordis 入门深入讲解它。schemastery+cosmokit——插件Configschema 写成z.object({…})(schemastery),cosmokit 提供工具。cordis自身用 cosmokit 与 schemastery。loader——把条目列表(cordis.yml)变成运行中插件图的EntryTree。dsh的配置系统(profile bundle、cordis.patch.yml、--patch覆盖层)建立在 Loader + Include 之上。include——以配置文件为后端的 loader 树;include 定义让用户配置覆盖默认值的补丁层。为 dsh 的事务化配置重载语义做了大量打补丁。hmr——监视文件,只重载依赖某变更应用文件的插件条目;框架级变更回退到loader.exit()(进程重启)。group——嵌套组,使条目可以挂载子项。timer——可 dispose 的定时器,挂载于 base/spine 组合。(schedule 特性不建立在它之上:@deepseek-ai/dsh-schedule运行自己的setTimeout/clearTimeout循环。)logger-console——把内置 logger 路由到控制台;ACP 服务器刻意不挂载它,使 stdout 保持协议纯净。
如何被消费
每个 harness 包把 @deepseek-ai/cordis(以及有时还有插件兄弟)声明为 peer 依赖;pnpm-workspace.yaml#linkWorkspacePackages 与 cosmokit/schemastery 的 overrides 把它们钉到拼接 workspace。消费者如同导入普通作用域包一样使用它们:
import type { Context } from '@deepseek-ai/cordis'
import Timer from '@deepseek-ai/cordis-plugin-timer'
import z from '@deepseek-ai/schemastery'拼接包的第三方依赖(如 @standard-schema/spec、js-yaml、chokidar、picomatch)留在 npm 上,不拼接。
本地修改:诚实且深入
vendor/README.md 维护一份穷尽的本地修改日志——每一处与上游的差异。值得注意的 dsh 专项加固:include/group/loader 的事务化配置对账(回滚实时更新、串行子树变更)、hmr 的精确配置监视与串行化刷新(含死锁修复)、引入上游 PR cordiverse/cordis#41(惰性配置解析),以及 cordis/src/fiber.ts 的生命周期加固——它补上三个可重入 dispose 漏洞:在 setup 体运行前登记 effect 的 owner-list 包装器、异步清理等待静止(quiescence)、在 owner 处于 UNLOADING 时拒绝创建 effect。
这些条目会引用下游测试,让评审者能找到覆盖。例如,Include 的串行化写入与 HMR 扫描抑制加固“Covered by the patch-overlay boot-failure built-bin case in apps/cli/tests/built-bin.e2e.ts”,持久化的 Include 写入则由“packages/host/directory-picker-auto/tests/loader-composition.spec.ts with injected transient and terminal rename failures”覆盖。有些修改纯属务实——重新生成的 package.json/tsconfig.json、为双 ESM+CJS 输出提供的逐包 tsdown.config.ts 覆盖(schemastery、logger-console),以及补充 @param/@returns JSDoc 以便网站 API 参考生成器能干净地报错。
vendor/ 下的 AGENTS.md 警示贡献者:不要随意改动 vendor/*/src/;每处本地差异都必须穷尽地记入 vendor/README.md。这正是让拼接层在跨同步时可审计的关键。
作为 peer 依赖被消费
因为每个消费者都把框架声明为 peer(这样发布时会解析到已发布的 scoped 名),挂载某个 Cordis 插件的包就会在自己的 package.json 里这样声明。不变量服务是个典型例子:
// packages/runtime-diagnostics/invariants/package.json(节选)
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
}注意分工:cordis 是 peer(其类型面定义 Context),而 schemastery 只是本包 Config schema 用到的普通 dependency。
同步流程
要从上游刷新某个拼接包,README 规定:记下上游 git rev-parse HEAD,复制包的 src/(以及 bin.js/README.md/LICENSE 若变动),重新应用本地修改(或若上游使其不再必要则删去——两种都要更新日志),更新清单表中的版本与 commit 哈希,然后 pnpm install && pnpm run test && pnpm run build。因此每处差异都带一个刻意的重新应用步骤,而非被静默合并。
@deepseek-ai 改名与 hygiene 保障
改名是被强制而非假设的。scripts/rescope-vendor.ts 重写每个拼接 manifest 的 name、内部依赖项与模块说明符为 scoped 名(一次同步后用 pnpm run rescope-vendor --apply 重新应用);pnpm run rescope-vendor:check 校验没有未 scoped 的引用泄漏回来。随后 hygiene 组合用 verify-vendored-links(每个名字解析为 link: workspace、无注册表副本)与 verify-cordis-config 确认整条链。因为 pnpm-workspace.yaml 设置了 linkWorkspacePackages: true,来自某包已构建 lib/ 的导入同样落在钉住的 workspace 源码上,而不是一次 npm 解析。