better-dsh 0.2.2-c → 0.2.3-b

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/docs/50_test-reports/2026-09-06-write/345/267/245/345/205/267sandbox/345/215/207/347/272/247/351/200/217/344/274/240bug/345/244/215/345/217/221/345/217/212/346/214/202/350/265/267-/344/272/213/344/273/266/346/212/245/345/221/212.md +133 -0
  2. package/docs/50_test-reports/2026-09-08-hashline-edit-E_RANGE_UNVERIFIED/350/267/250/350/275/256/344/274/232/350/257/235/351/224/256/345/244/261/346/225/210-/350/257/212/346/226/255/346/212/245/345/221/212.md +226 -0
  3. package/docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-local-test-report.md +44 -0
  4. package/docs/50_test-reports/upstream-dsh-0.1.3-alpha.2-report.md +110 -0
  5. package/docs/50_test-reports/v0.2.3b-hashline-content-locator/345/256/236/346/265/213/346/212/245/345/221/212.md +73 -0
  6. package/docs/60_exploration-and-research/01-cordis-runtime/cordis-engineering-feasibility.md +155 -0
  7. package/docs/60_exploration-and-research/01-cordis-runtime/cordis-research.md +385 -152
  8. package/docs/60_exploration-and-research/01-cordis-runtime/js-ts-language-fundamentals.md +628 -0
  9. package/docs/60_exploration-and-research/02-dsh/dash-research.md +592 -0
  10. package/docs/60_exploration-and-research/02-dsh/dsh-web-profile-package-map.md +186 -0
  11. package/docs/60_exploration-and-research/02-dsh/dsh-web-ui-slot-system-research.md +310 -0
  12. package/docs/60_exploration-and-research/02-dsh/dsh-webui-backend-data-inventory.md +633 -0
  13. package/docs/60_exploration-and-research/02-dsh/dsh-webui-strip-boundary-research.md +300 -0
  14. package/docs/60_exploration-and-research/02-dsh/dsh-webui-wire-appendix.md +3729 -0
  15. package/docs/60_exploration-and-research/02-dsh/web-frontend-composability-research.md +191 -0
  16. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/capture-live-turn.json +1 -0
  17. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/observed-endpoints.json +224 -0
  18. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/remote-inventory.json +110954 -0
  19. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/served-index-sample.html +47 -0
  20. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/session-events.json +9411 -0
  21. package/docs/60_exploration-and-research/02-dsh/webui-wire-data/ws-frame-examples.json +20 -0
  22. package/docs/superd/00-blueprint.md +331 -0
  23. package/docs/superd/01-component-boundaries.md +261 -0
  24. package/docs/superd/02-dsh-embed-modes.md +151 -0
  25. package/docs/superd/image.png +0 -0
  26. package/lib/index.js +1214 -1293
  27. package/package.json +1 -1
@@ -1,9 +1,15 @@
1
- # Cordis 框架研究
1
+ # Cordis 框架研究(核心)
2
2
 
3
- > 记录:2026-08-21 · 一手核验:vendored `@deepseek-ai/cordis` 4.x(本地 4.0.1)源码 +
3
+ > 记录:2026-08-21 · 2026-09-06 修订:剥离工程化旁支(Bun 构建 / 异构语言桥 / Rust 重写 /
4
+ > Recursive Cordis → `cordis-engineering-feasibility.md`),本文专注 **Cordis 核心机制**;
5
+ > 新增 §3.2 七名词的编程语言形态分类、§4 Fiber 运行全景、§5 时空再审视、§6 TS 机制答疑
6
+ > (均对 vendored 源码一手核验)。
7
+ > 一手核验:vendored `@deepseek-ai/cordis` 4.x(本地 4.0.1)源码 +
4
8
  > `github.com/cordiverse/cordis`(6770★)+ `github.com/cordiverse/paper`(2532★)+
5
9
  > dsh 官方 docs(`master` @ 2026-08-19)+ dsh `vendor/README.md`。
6
- > 本文是 Cordis 单侧研究(与 Dash 解耦后的文档;Dash 侧见 `dash-research.md`)。
10
+ > 本文是 Cordis 单侧研究(与 Dash 解耦后的文档;Dash 侧见 `dash-research.md`;
11
+ > 工程化旁支见 `cordis-engineering-feasibility.md`;JS/TS 语言基础伴生讲义见
12
+ > `js-ts-language-fundamentals.md`)。
7
13
 
8
14
  ---
9
15
 
@@ -42,10 +48,13 @@
42
48
  核心包 9 个源文件(`context/service/fiber/events/registry/reflect/logger/utils/index`,
43
49
  **实测 2693 行**)。四个构件落地时空可组合性:
44
50
 
45
- - **`Context`** = 运行时代理(proxy):属性读取走服务解析器(`ctx.tools`/`ctx.llm` 由服务名
46
- 解析,非 import 具体实现)。`extend()`/`isolate()`/`intercept()` 创建作用域子 context。
47
- - **`Fiber`** = 一个插件应用的运行时。持有 `_disposables`(DisposableList)、`inject`(依赖
48
- 声明)、`store`(已解析实现)、`state`(FiberState)、`epoch`(依赖满足度签名)。
51
+ - **`Context`** = 运行时代理:一个 JS 对象(类实例),但被 `new Proxy()` 包装——属性读取
52
+ 不在编译期绑定,而是读取那一刻走服务解析器(`ctx.tools`/`ctx.llm` 由服务名解析当前活跃
53
+ 提供者,非 import 具体实现)。详见 §3.2 与 §4.1。`extend()`/`isolate()`/`intercept()`
54
+ 创建作用域子 context。
55
+ - **`Fiber`** = 一个插件应用的运行时(实例记录 + 状态机)。持有 `_disposables`
56
+ (DisposableList)、`inject`(依赖声明)、`store`(已解析实现)、`state`(FiberState)、
57
+ `epoch`(依赖满足度签名)。
49
58
  - **时间维** `Fiber.effect(execute, label)`:`execute` 立即执行,产出 disposer 被收集;
50
59
  disposer 被调 **或** fiber 卸载时,disposers **逆注册序(LIFO)** 执行(
51
60
  `disposables.splice(0).reverse()`)。
@@ -69,8 +78,6 @@ Koishi:4 年、4000+ 社区插件、作者互不相识,唯一协调机制 =
69
78
 
70
79
  ### 你的理解的校验(逐点)
71
80
 
72
- 你的 mental model 基本正确,有两处需要精确化:
73
-
74
81
  > "基底 binary 可以看作一个入口"
75
82
 
76
83
  ✅ 精确。`dsh` 这个 binary 是 `apps/cli` 产出的 `lib/bin.js`,官方自述为 **"a thin
@@ -121,7 +128,7 @@ dsh 二进制(lib/bin.js,薄壳)
121
128
 
122
129
  ## 3. 架构结构测绘
123
130
 
124
- ### Monorepo 包结构(`cordiverse/cordis`,9 包)
131
+ ### 3.1 Monorepo 包结构(`cordiverse/cordis`,9 包)
125
132
 
126
133
  ```
127
134
  cordis/
@@ -137,175 +144,401 @@ cordis/
137
144
  └── utils/ → 共享工具
138
145
  ```
139
146
 
140
- ### 核心组件与关系
147
+ 只有 `core/` 是框架本体;其余全是**用框架 API 写的普通插件**("关于插件的插件",见 §3.4)。
141
148
 
142
- ```mermaid
143
- flowchart TD
144
- C["Context<br/>运行时 proxy"] --> RF["ReflectService<br/>provide/inject 服务解析器"]
145
- C --> RG["RegistryService<br/>plugin 注册 + Inject"]
146
- C --> EV["EventsService<br/>emit/parallel/serial/bail/waterfall"]
147
- C --> LG["LoggerService"]
148
- RG -->|"ctx.plugin() → Fiber"| F["Fiber<br/>一个插件实例的运行时"]
149
- RF -->|"super(ctx,name) 注册"| S["Service<br/>服务基类"]
150
- S -->|"随 fiber 卸载自动注销"| F
151
- F -->|"effect() 收集 disposer → 逆序撤销(时间维)"| UT["utils / DisposableList"]
152
- F -->|"inject + epoch → 激活/停用(空间维)"| RF
153
- L["loader<br/>import() 模块 → ctx.registry.plugin"] --> RG
154
- I["include<br/>配置子树挂载"] --> L
155
- H["hmr<br/>热重载"] --> F
156
- ```
149
+ ### 3.2 核心七名词的编程语言形态分类(答疑)
157
150
 
158
- ---
159
-
160
- ## 4. 开发语言与打包编译
151
+ **分类标准**(你的框架,予以保留):**对象 = 名词**(内存里的一坨数据+行为);
152
+ **函数 = 动词**(可独立调用的可执行体);**方法 = 挂在对象下的可执行函数**
153
+ (如 `context.load`);**类 = 蓝本**(生产对象的模板,本身也是对象)。
161
154
 
162
- ### 4.1 进程内约束(TS-only,Node ESM)
155
+ 先给结论,再逐项展开:
163
156
 
164
- - `cordis/package.json`:`"type": "module"`、`"main": "lib/index.js"`;依赖仅
165
- `@standard-schema/spec` + `cosmokit`——**零 native addon、零 WASM、零 child_process**。
166
- - 插件入口只有三种形状:`Function(ctx, config)` / `Constructor` / `Object{apply}`,必须求值
167
- 为 JS 可调用对象。
168
- - 插件加载:`tree.import(specifier)` → `unwrapExports()` → `ctx.registry.plugin(...)`。
169
- `ModuleFormat = 'builtin'|'commonjs'|'json'|'module'|'wasm'`(仅 Node loader 格式)。
157
+ | 名词 | 源文件 | 运行时语言形态 | 一句话本质 |
158
+ |---|---|---|---|
159
+ | **Context** | `context.ts` | **对象**(类实例,且被 Proxy 二次包装) | 根容器 + 开放命名空间;一切挂载点 |
160
+ | **Fiber** | `fiber.ts` | **对象**(类实例,每个 `ctx.plugin()` 调用一个) | 一个插件实例的运行时记录(状态机 + 撤销清单 + 依赖签名) |
161
+ | **Service** | `service.ts` | **类**(abstract 蓝本)→ 实例是**对象** | "以服务形式挂上 ctx 的对象"的基类模式;构造即注册 |
162
+ | **Events** | `events.ts` | **对象**(`ctx.events`,普通类实例) | 事件总线;`on/emit/parallel/serial/bail/waterfall` |
163
+ | **Registry** | `registry.ts` | **对象**(`ctx.registry`,普通类实例) | 插件登记簿(`Map<callback, Runtime>`);`ctx.plugin/inject` 的本体 |
164
+ | **Reflect** | `reflect.ts` | **对象**(`ctx.reflect`)+ **static ProxyHandler** | 服务解析器 + 代理陷阱层;让 Context 成为"运行时代理"的那个机制 |
165
+ | **Logger** | `logger.ts` | **对象**(**可调用对象**:函数+对象混合体) | 日志工厂;`ctx.logger(name)` 调用服务本身,返回具名 Logger 门面 |
166
+
167
+ 要点先行——**七个全是"名词"(对象/类),没有一个是独立函数;你感受到的所有"动词"
168
+ (`ctx.plugin()`、`ctx.on()`、`ctx.effect()`)都是方法,且是"投影出来的方法"**(见下)。
169
+
170
+ **两级视图(把"类"与"实例"分开说——你的第二次逼近成立)**:`Context` 是类(蓝本),
171
+ `new Context()` 产出的 `ctx` 才是实例(对象);`Fiber` 是类,每次 `ctx.plugin()` 产出一个
172
+ Fiber 实例(注意:`ctx.plugin` 本身是方法——RegistryService 投影,它的**返回值**才是那个
173
+ Fiber 实例);`Service` 是类,由提供方插件 new 出服务实例。⚠️ 术语更正:`new`/`extends`/
174
+ `instanceof` 是 JS 语言关键字,但 `extend()`/`isolate()`/`intercept()` **不是保留字**——
175
+ 它们是 Cordis 作者写在 Context 类上的普通方法,恰好起了这些名字。对类的"原操作"只有
176
+ 语言关键字那几个;其余全是类自定义方法。
177
+ #### (a) Context:一个对象,而且是"被代理的对象"
178
+
179
+ 用 JS 语言说清楚:
180
+
181
+ 1. `class Context` 是一个类(蓝本)。`new Context()` 产出实例——这就是你说的"一个对象"。
182
+ 2. 但构造函数最后一行 `return self`,其中 `self = new Proxy(this, ReflectService.handler)`
183
+ (`context.ts` 构造函数)。**JS 的 `Proxy` 是语言内置的"拦截器"**:包装后,外界对 ctx
184
+ 的一切属性读写都先经过 `ReflectService.handler` 的 `get`/`set`/`has` 陷阱函数。
185
+ 3. 陷阱的逻辑(`reflect.ts` handler.get):自身属性(`events/logger/registry/reflect/root`
186
+ 等)直通 → 声明过的 accessor 走 accessor → 其余属性名被当作**服务名**,沿 fiber 链向上
187
+ 查询"当前作用域内谁是这个服务名的活跃提供者"(尊重 isolate 作用域)→ 查不到就抛
188
+ `cannot get property "..." without inject`。
189
+
190
+ 所以"**运行时代理**"的准确含义是:**属性读取不在编译期绑定到具体实现,而是在读取那一刻
191
+ 由运行时查询服务登记表来解析**。你的理解"它是一个对象、所有插件/服务都挂在这个对象下面"
192
+ 对了前一半(挂载确实以此为根),但"挂"不是静态属性赋值——服务是登记进 `ReflectService`
193
+ 的 store,经 Proxy 按名动态解析。这带来关键后果:**同一个表达式 `ctx.llm` 在不同时刻可以
194
+ 解析到不同对象**(提供者换人、服务热替换),这是"免重启"的语言层基础。
195
+
196
+ 另一个易误解点:ctx 上看起来"什么方法都有"(`ctx.on`/`ctx.plugin`/`ctx.get`/`ctx.effect`),
197
+ 像一个大杂烩上帝对象。实际上 **Context 类自身只定义了三个方法:`extend()`/`isolate()`/
198
+ `intercept()`**(外加 `Context.is()` 等静态成员)。其余所有"方法"都是四个内建服务的方法经
199
+ `ctx.mixin()` **投影**到 ctx 命名空间上的 accessor(见 (d)-(f))。
200
+
201
+ **符号键的跨副本根基**(2026-09-07 补,外部分析校验时发现):框架内部符号**全部**用
202
+ `Symbol.for('cordis.*')`(全局注册表符号,utils.ts L50–73)定义——同一进程装入两份
203
+ cordis 副本(vendored + npm)时符号仍相等,互操作不碎;`Context.is` 品牌、
204
+ `[symbols.isolate]` 等内部槽全靠它。类型侧用 `as typeof Context.effect` 把值拴到类静态
205
+ 声明的 `unique symbol`(unique symbol 的核心用途:让 symbol 能做 interface 计算键)。
206
+ 详见伴生讲义 §20。
207
+
208
+ #### (b) Fiber:一个对象——插件实例的"运行时档案 + 状态机"
209
+
210
+ 不是函数、不是方法,是纯对象(类实例)。每次调用 `ctx.plugin(插件, 配置)` 就 `new Fiber(...)`
211
+ 一个(`registry.ts` L296)。它记录:
212
+
213
+ - `uid`(registry 发的唯一序号;根 fiber 为 0)、`ctx`(该插件专属子 context)、
214
+ `config`(校验后配置)、`inject`(依赖声明表)、`store`(已解析的服务实现快照)、
215
+ `state`(PENDING/LOADING/ACTIVE/FAILED/DISPOSED/UNLOADING 六态)、
216
+ `_disposables`(disposer 清单)、`_runner.epoch`(依赖满足度签名)。
217
+ - 它自己的方法:`effect()`(登记可撤销效应)、`restart()`、`update()`、`await()`、`dispose`。
218
+
219
+ **真正的"动词"不是 Fiber,而是它包裹的插件回调**:`_execute` → `runtime.callback(ctx,
220
+ config)`(函数插件直接调用 / 类插件 `new`)。Fiber 是这个动词的**执行上下文与档案**——
221
+ 你的"Fiber 本质上是一个插件实例的运行时"这句判断完全成立。
222
+
223
+ #### (c) Service:一个类(蓝本)——"自我登记的对象"模式
224
+
225
+ `abstract class Service` 是给**插件作者**继承的基类。子类构造函数里 `super(ctx, 'name')`
226
+ 做两件事:把实例挂为 `this.ctx`、调用 `ctx.reflect.provide(name, this)` **把自己登记为
227
+ 该名字的服务提供者**——随所属 fiber 卸载自动注销。所以 Service 是一个**模式**:
228
+ "构造即登记、随宿主撤销"的对象。它不是函数也不是方法;它的实例是对象(特殊情形:声明了
229
+ `[Service.invoke]` 的服务实例会被包装成**可调用对象**——函数与对象的混合体,如
230
+ `ctx.logger('name')` 就是"调用服务对象本身")。
231
+
232
+ 注意区分:**四个内建服务(Events/Registry/Reflect/Logger)并不继承 Service,也不是插件**
233
+ ——它们由 `new Context()` 构造函数直接 `new` 出来装进根容器,属于框架基底。`Service` 基类
234
+ 是给此后挂上来的东西用的(Loader 就是 `Service` 的子类,见 §3.4)。
235
+
236
+ 同时 **Service 也不是 Context 的子类**:`abstract class Service` 不 `extends Context`,
237
+ TS 也没有"类里嵌类"这种结构——它通过构造参数 `(ctx, name)` **持有**一个 ctx 引用
238
+ (存为 `this.ctx`),是**组合(has-a)而非继承(is-a)**。dsh 实证:`AgentRegistry
239
+ extends Service`(`agents`/`subagents` 提供者)、`LlmRuntime extends TypertRemoteService
240
+ extends Service`(`llm` 提供者)。
241
+
242
+ #### (d) Events:一个对象——事件总线
243
+
244
+ `export class EventsService`(普通类),实例挂为 `ctx.events`。持有一张 `_hooks` 表
245
+ (事件名 → 监听器数组)。核心方法 `on/once/emit/parallel/serial/bail/waterfall`——这七个
246
+ 方法经 `ctx.mixin('events', [...])` 投影为 `ctx.on(...)` 等(`reflect.ts` 构造函数里)。
247
+ 事件监听器随登记它的 fiber 卸载而自动移除(又一个 effect)。
248
+
249
+ #### (e) Registry:一个对象——插件登记簿
250
+
251
+ `export class RegistryService`(普通类),实例挂为 `ctx.registry`。内部一张
252
+ `Map<插件回调函数, Plugin.Runtime>`(Runtime = `{name, callback, fibers, Config}`,同一插件的
253
+ 多次挂载共享一条 Runtime 记录、各有一个 Fiber)。核心方法 `plugin()` / `inject()`——前者就是
254
+ "挂插件"的动词入口:校验形状 → 建/查 Runtime → `new Fiber(...)`。经 mixin 投影为
255
+ `ctx.plugin()` / `ctx.inject()`。
256
+
257
+ #### (f) Reflect:一个对象 + 一段静态 ProxyHandler——服务解析器(最核心的一个)
258
+
259
+ `export class ReflectService`(普通类),实例挂为 `ctx.reflect`。它持有:
260
+
261
+ - `static handler`:那个 Proxy 陷阱对象——**Context 之所以是"运行时代理",机制全在这里**;
262
+ - `store`:服务实现登记表 `Dict<Impl>`(Impl = `{name, fiber, value, check}`,按 isolate
263
+ 符号键分作用域);
264
+ - `props`:ctx 属性定义表(service / accessor 两类);
265
+ - 方法 `provide/get/set/accessor/mixin/notify`——`notify` 是空间维的**心脏**:某服务变更时
266
+ 遍历 registry 里所有 inject 了该服务的 fiber,逐个重算依赖与 epoch(见 §4.3)。
267
+
268
+ ⚠️ 命名陷阱:它与 JS 内置的 `Reflect` 全局对象**无关**。"reflection" 取元编程之意——
269
+ 拦截属性访问、把"名字 → 实现"的解析变成运行时数据。Plugin Include 等机制确实经它做服务
270
+ 解析,但 include 的主体机制是配置组合(见 §3.4 的修正)。
271
+
272
+ #### (g) Logger:一个可调用对象——日志工厂
273
+
274
+ `LoggerService` 实例被 `createCallable` 包装成**可调用对象**(函数+对象混合体):调用
275
+ `ctx.logger('my-plugin')` 就是调用服务对象本身(调用体 = `LoggerService[symbols.invoke](name)`,
276
+ `logger.ts`),返回一个具名 `Logger` 门面对象
277
+ (`error/info/warn/debug` 四个方法 + 着色/格式化)。`Logger` 门面也是对象;exporter 后端
278
+ (如 logger-console 包)另算插件。
279
+
280
+ #### 合起来的图景:"方法"是投影的错觉
281
+
282
+ 你在 ctx 上调用的每个"方法",真实归属是:
283
+
284
+ | 你写的调用 | 真实本体 | 投影机制 |
285
+ |---|---|---|
286
+ | `ctx.on/once/emit/parallel/serial/bail/waterfall` | `ctx.events.*`(EventsService 方法) | mixin accessor |
287
+ | `ctx.plugin(...)`, `ctx.inject(...)` | `ctx.registry.*`(RegistryService 方法) | mixin accessor |
288
+ | `ctx.get/set/provide/accessor/mixin` | `ctx.reflect.*`(ReflectService 方法) | mixin accessor |
289
+ | `ctx.effect(...)`, `ctx.runtime` | `ctx.fiber.*`(Fiber 的成员) | mixin accessor |
290
+ | `ctx.extend/isolate/intercept` | `Context` 类自身的方法 | 原型链 |
291
+ | `ctx.llm` / `ctx.tools` / 任何服务名 | ReflectService.store 里的当前活跃实现 | Proxy get 陷阱 |
170
292
 
171
- ### 4.2 打包编译引擎:能否换成 Bun
293
+ **运行时用 Proxy + mixin 把五个服务的表面"压平"成一个 ctx 命名空间;编译期用声明合并把
294
+ 五个模块的类型表面合并进一个 `Context` 接口(见 §6)——两层是精确对称的。**
172
295
 
173
- **结论:Cordis 核心(零 native 依赖)可 trivial 编译成 Bun 单文件可执行;dsh 全量表面
174
- "可行但有工程成本",不是"被卡死"。Claude Code 就是现成先例。**
296
+ #### 嵌套与递归:两种"套娃",别混为一谈
175
297
 
176
- | 项 | Bun 支持 | 说明 |
177
- |---|---|---|
178
- | `bun build --compile` 单文件可执行 | ✅ | 内嵌 Bun 运行时,客户端无需装 Node;`--target=bun-linux-x64` 交叉编译 |
179
- | `node:child_process`(spawn/PTY) | ✅ | 全支持 |
180
- | `node:worker_threads` | ✅(小缺口) | postMessage/SharedArrayBuffer 支持;resourceLimits/execArgv 部分 |
181
- | `node-pty`(native addon) | ❌ | 历史性坏(oven-sh/bun#7362);Bun 官方替代 = **Bun.Terminal**(v1.3.5 起) |
182
- | `node-addon-landlock-run`(N-API) | ⚠️ | N-API 可加载,但 `.node` 须**静态 require + 逐 target 预编译**才能嵌入 `--compile` |
183
- | tsdown(Rolldown+Oxc) | ⚠️ | 是 Rolldown+Oxc(非 esbuild);Bun 下运行是 experimental,跑 bundler 需 Node 22+ |
184
-
185
- **Claude Code 先例(前提确认为真)**:Anthropic 自 ~v2.1.113 起把 Claude Code 以
186
- **Bun 编译的 standalone 原生二进制**发布(`curl`/brew/winget 装的就是它;npm 包只是下载并
187
- 链接同一二进制的 wrapper,运行时不碰 Node)。**Anthropic 2025-12 收购了 Bun**,部分原因就是
188
- Claude Code 以这种方式 ship。所以"用 Bun 预编译 + 直接 ship binary"不是假设,是存在证明。
189
-
190
- **推荐落地路径**(与 Claude Code 一致):**195 个 npm 包继续用 tsdown 在 Node CI 构建
191
- (不动),只在最终 app 装配 + 编译阶段用 `bun build --compile`**。硬阻塞只有两个:
192
- `node-pty` → 换 `Bun.Terminal`;`node-addon-landlock-run` → 逐 OS/arch 预编译并静态 require。
298
+ 你的"可以无限嵌套下去"直觉在**运行时**成立,但要区分两层:
193
299
 
194
- ---
300
+ 1. **对象图嵌套(普通 JS,框架不管)**:`ctx.agents` 返回的对象有自己的方法,方法又
301
+ 返回 session/实例对象……任何对象图都能这样套,与 Cordis 无关。
302
+ 2. **框架级递归(Cordis 的分形结构)**:框架原语在**每一层**都可用——任何插件/服务的
303
+ 代码都能再 `ctx.plugin()`(fiber 树加深)、`ctx.extend()/isolate()`(context 链延长)、
304
+ `ctx.provide()`(服务图加深),且每一层都自动获得同样的 effect 收集与 epoch 生命周期。
305
+ Loader 挂 dsh 插件、dsh 插件再挂 per-session 子插件,就是这棵树在真实 App 里的深度。
195
306
 
196
- ## 5. 异构代码衔接层(Hetero-Language Bridge)
307
+ 类层面**没有**嵌套:TS 的类都是模块级的,`Service` 不定义在 `Context` 里面,也不继承它;
308
+ "嵌套"全部发生在**运行时对象图**(fiber 树 + context 原型链 + 服务引用)这一侧。
197
309
 
198
- ### 结论:官方**已经实现**了"异构语言成为插件模块",形式 = 进程边界 + IPC 桥
310
+ ### 3.3 七名词互动图
199
311
 
200
- 你对"其实是不是 subprocess 倒无所谓,只要逻辑层自洽、实现无感"的判断是对的——Cordis 的
201
- "everything is plugin" 抽象**确实自洽**:从 Cordis 视角看,`PythonCodeRuntime` 就是一个
202
- 普通 Service provider;它内部 spawn 一个 Python 子进程、用 fd3 桥接,是藏在 `ctx.codeRuntime`
203
- 缝隙背后的实现细节,消费者无感。
312
+ ```mermaid
313
+ flowchart TD
314
+ C["Context<br/>根容器对象(Proxy 包装)"] -->|"构造时装入"| RF["Reflect<br/>服务解析器 + ProxyHandler"]
315
+ C --> RG["Registry<br/>插件登记簿"]
316
+ C --> EV["Events<br/>事件总线"]
317
+ C --> LG["Logger<br/>可调用日志工厂"]
318
+ RG -->|"ctx.plugin() → new Fiber"| F["Fiber<br/>插件实例运行时(对象/状态机)"]
319
+ RG -->|"mixin 投影 ctx.plugin/inject"| C
320
+ RF -->|"mixin 投影 ctx.get/provide"| C
321
+ EV -->|"mixin 投影 ctx.on/emit"| C
322
+ S["Service<br/>服务基类(蓝本)"] -->|"super(ctx,name) → reflect.provide"| RF
323
+ S -->|"实例随 fiber 卸载自动注销"| F
324
+ F -->|"effect() 收集 disposer → LIFO 撤销(时间维)"| UT["utils / DisposableList"]
325
+ F -->|"inject + epoch → 激活/停用(空间维)"| RF
326
+ RF -->|"notify() 服务变更 → 重算受影响 fiber 的 epoch"| F
327
+ L["loader(插件,非核心)<br/>import() → ctx.registry.plugin"] --> RG
328
+ I["include(插件)<br/>配置子树挂载"] --> L
329
+ H["hmr(插件)<br/>模块变更 → fiber 重载"] --> F
330
+ G["group(插件)<br/>isolate realm 分组"] --> C
331
+ ```
204
332
 
205
- ### 官方实现(`dsh-code-runtime-python` 的 fd3 帧协议)
333
+ ### 3.4 插件包层:loader / include / hmr / group——"关于插件的插件"
206
334
 
207
- 仓库里**真实实现**(非 stub):`packages/code-runtime/code-runtime-python/`(`src/index.ts`
208
- + `py/protocol.py` + e2e tests)。机制:
335
+ 你的理解框架**成立**:核心定义完 7 个名词后即自包含;loader/include/hmr/group 这些包
336
+ (发布名 `@cordisjs/plugin-*`)都是**用核心 API 写的普通插件**,解决"非核心的外来插件怎样
337
+ 融入根容器"。逐个校验(以 dsh vendored 的 `@deepseek-ai/cordis-plugin-loader` 一手核验):
209
338
 
210
- - 每个 model program 跑在**全新 `python3 -I` 子进程**里;`stdio: [pipe, pipe, pipe, pipe]`
211
- 的第 4 项 = **fd 3**,作为 framed-JSON 通道;stdout/stderr 留给程序自己的输出。
212
- - **帧 = fd 3 上的 JSON-lines**(每行一个 JSON 对象)。子→宿主:`boot-ack`/`call`/`log`/
213
- `done`;宿主→子:`boot`(首帧)/`run`(`boot-ack` 后)/每 `call` 一个 `reply`。
214
- - **宿主把每帧都当敌对输入**(`validateChildFrame` 逐字段校验 + 重建):model 代码对 fd 3
215
- 有完全访问权、可伪造任意帧,所以进站的 forged 字段被丢弃、非有限 call id 不会回显。
216
- - **无损 JSON codec**(无 `JSON.stringify` 深度限制;迭代遍历;超安全整数走 `BigInt`;
217
- 字节计量),保证 `CodeJsonValue` 深度无界也能过线。
218
- - `py/protocol.py` 是 TS `src/protocol.ts` 的镜像:`TypedDict` 形状 + `PROTOCOL_FD = 3` +
219
- `log_truncation_marker`(**字节级一致**);`protocol-mirror.e2e.ts` 起真实 `python3` 断言
220
- 两侧字段名/必填性不漂移(曾因 round-12 三次字段漂移而加此守卫)。
339
+ | 包 | 你的理解 | 校验与精确化(源码锚点) |
340
+ |---|---|---|
341
+ | **loader** | 扫描非核心插件、有就加载进来;互动根容器是 Context,对应方法是 Registry | ✅ 精确。`Loader` 类**本身是 Service 子类**:构造时 `ctx.reflect.provide('loader', this)`——装载器自己也是一个服务插件(dsh 里由 `dsh-app-boot` `ctx.plugin(Loader)` 挂载为 `cordis:include` 内建)。它读配置树(YAML/JSON 行)→ 每行一个 `Entry` 节点 → `Entry` 里 `tree.import(name)` 动态 `import()` 模块 → `unwrapExports()` 归一 → **`ctx.registry.plugin(plugin, config)`**(`config/entry.ts` L296)——最后一跳正是你说的"对应的方法就是 Registry" |
342
+ | **group** | 就是一个子 Context;核心里 isolate 等本质也是子 Context | ✅ 成立。核心 `Context.isolate(name, label)` 本来就产子 context(`extend()` 出原型链子对象 + 独立 isolate 映射);group 包在这之上做**配置化封装**:`EntryGroup` 管一组子条目,`Realm`(LocalRealm/GlobalRealm)给服务名分配符号作用域——即"一组插件在独立 realm 里互见、与外界隔离"。所以:**子 Context 是核心机制,group 是它的声明式糖** |
343
+ | **include** | 依托核心里的 Reflect 实现 include | ⚠️ 需修正主体:include 解决的是**配置组合**——把外部配置子树(另一份 YAML、`!!js` 表达式求值)并进当前插件树,然后把实际加载委托给 Loader → Registry。它依托的主干是 **Context 的作用域机制(`extend`/`intercept`)+ Registry/Loader**;Reflect 只在最后的"服务解析"一环出场(任何服务消费都经它,但这不是 include 的特性)。一句话:**include = 配置层的组合子;服务解析层的组合子是 Reflect** |
344
+ | **hmr** | (未展开) | 模块文件变更 → dispose 受影响 Runtime 的 fibers → 重新 import + `ctx.plugin()` 重挂(loader 源码注释"plugin hmr: delete(plugin) -> runtime dispose -> fiber dispose")。核心侧对应原语就是 `Fiber.restart()` / epoch 重算——hmr 没有自己的新机制,纯调度 |
221
345
 
222
- TS 侧 `src/index.ts` 把 `PythonCodeRuntime` 注册为 `ctx.codeRuntime` 的 provider——**这就是
223
- "Python 成为 Cordis 插件"的官方答案:TS 宿主侧插件 + 外语子进程 + fd3 JSON-lines IPC**。
346
+ **统一图景**(你的表述可直接采用):App 的 entry point 是 Core(`new Context()` 完成基础
347
+ 准备——定义七个名词、装入四服务、造根 fiber);此后一切都以插件形式从根容器长出来:
348
+ Loader 负责"发现并加载",include 负责"配置怎么拼",group 负责"挂到哪个作用域",hmr 负责
349
+ "变了就重挂"。**它们互相也只是对方的插件/服务,无一是特权**。
224
350
 
225
- ### 全部异构缝隙(进程边界是唯一的跨语言方式)
351
+ ### 3.5 进程内约束(TS-only,Node ESM)
226
352
 
227
- | 缝隙 | 协议 | 边界 | 可达语言 |
228
- |---|---|---|---|
229
- | `ctx.subprocess`(subprocess-local) | stdio + 进程组 | `node:child_process`+node-pty | 任意可执行 |
230
- | `ctx.shell`(bash/pwsh-local) | `bash -c`/pwsh | 出进程 | 任意命令 |
231
- | `dsh-mcp-client` | MCP = JSON-RPC 2.0(stdio / Streamable HTTP) | 出进程/远程 | 语言无关(Python MCP SDK 成熟) |
232
- | `ctx.codeRuntime`(code-runtime-python) | fd3 framed-JSON-lines | Python 子进程 | Python |
233
- | `dsh-sandbox`/`fs-sandbox`/`bash-sandbox` | 策略包装 subprocess/fs | 出进程 | 底层缝隙可达的任意语言 |
234
- | `dsh-typert-*` | Typert RPC 装饰器 + endpoint registry | 进程内 TS 反射 | 仅 TS |
353
+ - `cordis/package.json`:`"type": "module"`、`"main": "lib/index.js"`;依赖仅
354
+ `@standard-schema/spec` + `cosmokit`——**零 native addon、零 WASM、零 child_process**。
355
+ - 插件入口只有三种形状:`Function(ctx, config)` / `Constructor` / `Object{apply}`,必须求值
356
+ 为 JS 可调用对象(`RegistryService.resolve()` 只认这三类)。
357
+ - 插件加载:`tree.import(specifier)` → `unwrapExports()` → `ctx.registry.plugin(...)`。
358
+ `ModuleFormat = 'builtin'|'commonjs'|'json'|'module'|'wasm'`(仅 Node loader 格式)。
235
359
 
236
- > 注意:默认 `web` profile 只 bundle `dsh-code-runtime-worker-thread`(TS 后端);Python 后端
237
- > 在仓库里但不随默认 profile 发布,需显式安装。
360
+ > 打包编译(换 Bun)、异构语言桥、Rust 重写等工程化可行性研究已剥离至
361
+ > `cordis-engineering-feasibility.md`。
238
362
 
239
363
  ---
240
364
 
241
- ## 6. Rust 重写
242
-
243
- ### 结论:核心重写**已有人做完**;但"Rust Cordis 兼容纯 TS 插件"不是进程内可行的,"万能"的
244
- 真相是"TS 插件变成又一种异构语言桥"
245
-
246
- - **Cordis 源码量**:核心实测 **2693 行** TS(9 文件);上游仓库总 2590 KB(含 docs/tests)。
247
- - **`cordis-rs` 已经存在**(`docs.rs/cordis-rs`):`@deepseek-ai/cordis` 4.x 的 Rust 移植,
248
- 证明**原生核心(作用域 DI / 生命周期拥有的效应 / Fiber 状态机 / 事件总线 / registry /
249
- reflect / logger)能干净地移植到 Rust**。Rust 插件走 `plugin_sync`/`plugin_async`,无 JS 引擎。
250
- - **异构语言插件**:当前最佳实践 = **WASM Component Model(wasmtime + WIT/bindgen)**;
251
- **Extism** 作为便捷多语言 PDK 层(Rust/Python/Go/JS/TS/C#/Zig/C/C++)。
252
- - **"兼容纯 TS Cordis 插件"的真相**:**进程内不现实**。现有 Cordis/dsh 插件是 Node 程序
253
- (`node:child_process`/`worker_threads`/`node-pty` native addon/ESM import `cordis`/ctx proxy/
254
- inject epoch/热重载)。**没有任何嵌入式 JS 引擎**能提供这套 Node 面——`deno_core`(V8) 默认
255
- 无 Node built-ins;QuickJS/Boa 无 Node API;Extism JS PDK(QuickJS-ng in WASM)明确无
256
- 事件循环/`child_process`/Worker/fs/net、sync-per-export。因此纯 TS 插件必须**在真实
257
- Node/Bun 运行时里出进程跑,走 RPC 桥**——即第 5 节的同一种异构桥,恰好是"又多一种插件类型"。
258
-
259
- | 机制 | 跨语言 | 能跑现有 JS 插件? | 先例 |
260
- |---|---|---|---|
261
- | 原生 Rust 核心(cordis-rs) | 仅 Rust | ❌ | cordis-rs(已 ship) |
262
- | WASM Component Model(wasmtime) | ✅(WIT 多语言) | ❌ | wasmtime 生态 |
263
- | Extism PDK | ✅(Rust/Py/Go/JS/C#...) | ❌(JS PDK 无 Node 面) | Extism |
264
- | 嵌入式 V8(deno_core)/QuickJS/Boa | 仅 JS 子集 | ⚠️ 须重实现 `cordis` + ctx proxy + inject epoch | 无先例 |
265
- | **出进程 Node/Bun + RPC 桥** | ✅(任意语言) | ✅(真实 Node/Bun 跑 TS 插件) | 第 5 节 fd3 / MCP |
365
+ ## 4. 运行全景:一个 Cordis App 从启动到自愈(答疑)
366
+
367
+ ### 4.1 启动链与"控制权移交"的准确位置
368
+
369
+ ```
370
+ Node 进程启动(bin 薄壳)
371
+ → new Context()
372
+ ├─ 造 Proxy 包装的根 ctx 对象
373
+ ├─ 构造四个内建服务:reflect → registry → events → logger
374
+ │ (ReflectService 构造函数顺手把四服务的方法 mixin 投影到 ctx 上)
375
+ └─ 造根 Fiber(uid=0,无 runtime,execute=空函数,天生 ACTIVE)
376
+ ——根 fiber 就是"框架自身的运行时档案",dispose 它 = 重启整个插件树
377
+ → app-boot 挂 Loader 插件(ctx.plugin(Loader),Loader provide 'loader' 服务)
378
+ → Loader 读配置树,逐行:
379
+ ctx.registry.plugin(插件模块, 行配置)
380
+ → new Fiber(...)(PENDING,专属子 ctx = parent.extend({fiber}))
381
+ → 逐个 inject 名字 _checkImpl() 查依赖 → _refresh() 算 epoch
382
+ → 依赖齐(epoch 非 INACTIVE)→ _reload() → _execute()
383
+ → runtime.callback(this.ctx, this.config) ← ★ 控制权移交时刻
384
+ (函数插件直接调用;类插件 new 之;此后跑的全是插件作者的代码)
385
+ → 插件代码里的 ctx.on()/ctx.provide()/... 每一笔都被记为该 fiber 的 effect
386
+ ```
266
387
 
267
- **诚实成本**:原生核心 8–15k LOC / 4–8 周达到对等;**JS 互操作是真正耗时数月的硬层**
268
- (要么在嵌入式引擎里重实现 Node API 面——无先例,`napi-rs` 是反方向;要么让响应式
269
- inject-epoch 语义跨进程边界保持一致)。所以正确的心智模型不是"Rust 版 Cordis 万能兼容 TS",
270
- 而是 **原生 Rust 核心 + TS 插件作为又一种异构语言类型(出进程 Node/Bun + 桥)**。
388
+ 你的模型"entry point 是 Core → Core 做基础准备 → 之后交给 Fiber 去运行 App 实例 → 每个
389
+ Fiber 运行起来就是把控制权交给该插件的代码"——**成立**,只需补一处精确:移交不是构造
390
+ Fiber 时发生的,而是 Fiber 的依赖检查通过、`_reload()` 执行 `_execute` 调到
391
+ `runtime.callback(ctx, config)` 那一刻。Fiber 创建后可能长期停在 PENDING(依赖未齐,
392
+ 代码一行不跑);依赖永远不齐就永远不跑。
393
+
394
+ ### 4.2 两层依赖检查:静态层在框架外,动态层在框架内
395
+
396
+ 你的两分法正确,边界再钉死一点:
397
+
398
+ 1. **静态层(Cordis 管不着,也不管)**:npm 包依赖、TS `import`、IDE 类型检查——发生在
399
+ 写码/构建期,由 Node 模块系统与 tsc 解决。Cordis 对此的唯一约定是:模块默认导出必须是
400
+ 三种插件形状之一。
401
+ 2. **动态层(Cordis 的领地)**:`inject` 声明(插件静态写明"我需要哪些服务名",**注意:
402
+ inject 声明的求值发生在插件加载前,但它检查的东西——服务的在场性——是运行时状态**)。
403
+ `import` 绑定"代码在哪",`inject` 绑定"运行时和谁耦合":前者编译期解析一次就死;
404
+ 后者每次服务变更都重查。这就是为什么换实现可以免重启——耦合点是名字,不是引用。
405
+
406
+ 补充一个中间工具:`ctx.get(name, strict=false)` 可以不声明 inject 就"偷看"服务(拿不到
407
+ 得 undefined,不会抛错)——用于可选依赖;但偷看不会建立响应关系,服务来了不会唤醒你。
408
+
409
+ ### 4.3 Effect 收集与撤销(时间维机制,源码级)
410
+
411
+ **"Effect 检查器"的准确身份:不是一个独立组件,而是 Fiber 内建的收集器**——
412
+ `_disposables`(DisposableList)+ `effect()` API + LIFO 撤销约定。没有第七个名词,它就是
413
+ Fiber 这个名词的一组方法。
414
+
415
+ - **登记**:`ctx.effect(execute, label)`(投影自 `ctx.fiber.effect`)。`execute` **立即执行**;
416
+ 其返回值被收集为 disposer——接受四种形状:单个函数 / Promise<函数> / 迭代器 /
417
+ 异步迭代器(**生成器写法支持流式登记**:每 yield 一个 disposer 立即生效,长生命周期任务
418
+ 可边跑边登记)。框架自身的一切上下文修改都走这条路:`ctx.on` 返回的注销函数、
419
+ `ctx.provide` 的注销、`ctx.mixin` 的拆除……全是 effect。
420
+ - **数据结构**:你猜"清单,或者可能是一个树状结构"——**两层都对**:运行时是**平面列表**
421
+ (DisposableList,Map 保序去重);每个 disposer 携带 `EffectMeta`(label + children)构成
422
+ **诊断树**(`fiber.getEffects()` 拉出来看,就是运行时 effect 的账本)。
423
+ - **撤销序**:LIFO 有两处一手实证——单 effect 撤销 `disposables.splice(0).reverse()`
424
+ (`fiber.ts` effect 内部);fiber 整体卸载 `DisposableList.clear()` → `values.reverse()`
425
+ (`utils.ts`)。语义:**后建立的依赖先拆**(典型如先拆"用连接的回调"再拆"连接本身",
426
+ 逆序保证不会拆到还在用的东西)。
427
+ - **单次性**:disposer 调第二次是 no-op;async disposer 会被 await。
428
+
429
+ ### 4.4 epoch:依赖满足度签名与"迁移"的真身(空间维机制)
430
+
431
+ 把你的描述逐句对到源码:
432
+
433
+ | 你的表述 | 源码机制 |
434
+ |---|---|
435
+ | "用 inject 语法,实际运行起来时它看到就去检查" | 服务登记/注销/换值 → `ctx.reflect.notify([名字])` → 遍历 registry 全部 Runtime 的全部 fiber,凡 inject 了该名字的:`fiber._checkImpl(名字)`(重新解析实现进 `_store`,含可用性谓词 `check()`)→ `fiber._refresh()` |
436
+ | "依赖签名(epoch 检查)" | `_refresh()` 重算签名:遍历 inject 名字,任一缺失 → `epoch = INACTIVE`(字符串哨兵);否则 `epoch = ':' + 提供者1.fiber.uid + ':' + 提供者2.fiber.uid ...`——**把"我依赖谁的哪一代"压缩成一个字符串指纹** |
437
+ | "不存在 → inactive → 动态清理/迁移出去" | `_setEpoch(新签名)` 与旧签名比较:变化则驱动状态机——`INACTIVE → 有效` = `_reload()`(执行插件回调);`有效 → INACTIVE` 或 **uid 变了**(':3'→':7',提供者换人)= `_unload()`(LIFO 跑光全部 disposer),卸完发现签名又有效则自动 `_reload()`。⚠️ 精确化:被"清理"的不是依赖声明(inject 表原样保留),而是**依赖者的全部副作用被撤销、状态回到 PENDING**;提供者回归(无论同一 fiber 复活还是新 fiber 顶上)后,依赖者**从头重放** apply——插件代码被重新执行一遍,拿到的是新提供者的引用。所谓"迁移"= **卸载-重放循环**,不是把对象搬家 |
438
+ | "如果存在就执行 apply / 还在的就继续" | 对,且更弱:Cordis 不做增量更新依赖者——只要提供者 uid 变了就整 fiber 重放(哪怕新旧实现 API 兼容)。这是"简单粗暴但正确"的取舍:重放成本换来了不用写迁移代码 |
439
+
440
+ **闭环**:提供者注销自身也是 effect(`ctx.provide` 的 disposer 里 `delete store[key]` +
441
+ `notify`),所以"拔插头"这个动作本身可逆、可追溯——时间维与空间维在此咬合:
442
+ **空间维的每次图变化,都通过时间维的撤销/重放机制落地**。
271
443
 
272
444
  ---
273
445
 
274
- ## 7. 进程内框架与 Recursive Cordis
446
+ ## 5. 时空再审视:"其实只有一个时间维"?——对一半,另一半要说准
447
+
448
+ 你的命题:"它实际运行起来其实只有一个时间维,只不过把空间维的'可组合性'(依赖、调用)
449
+ 放在动态运行时去自愈。"
450
+
451
+ **判定:执行层面的观察完全正确;但"空间维消失了/只是自愈"的推论要修正——空间维没有
452
+ 消失,而是换了存在形态:从"构建期静态结构"变成"运行期可增量重算的数据"。**
453
+
454
+ 论证:
455
+
456
+ 1. **执行确实只有一条时间线**。单进程单线程事件循环,一切(加载、provide、notify、
457
+ unload/reload、disposer)都排布在同一条因果链上。没有第二条执行流,"时间维是唯一的
458
+ 执行维度"成立。
459
+ 2. **但空间维作为数据真实在场**,且不可归约:
460
+ - `inject` 边集合(谁声明依赖谁)——决定 notify 波及谁;
461
+ - `store` + isolate 符号作用域——决定"谁能看见谁"(祖先可见、兄弟不可见、realm 隔离),
462
+ 这是**纯空间规则**,不随时间演化;
463
+ - `epoch` 本身就是空间状态的指纹——如果空间维不存在,这个签名无从算起。
464
+ 3. **准确的分工**(一句话版):
465
+ **时间维是机制(怎么撤销、怎么重放),空间维是数据(对谁、按什么拓扑);
466
+ 自愈 = 空间数据的一次变更事件,驱动时间维机制重放受影响的子图。**
467
+ 4. 与传统框架对比即见差异所在:NestJS/Angular 的空间维(DI 图)在构建/启动期定死,
468
+ 运行期只是查询;Cordis 把这张图**物化为运行时数据**(store/epoch),任何一次变化都能
469
+ 增量重算。类比:React 把 UI 从命令式操作变成 `UI = f(state)` 的派生数据;Cordis 把
470
+ **插件激活**变成 `激活 = f(依赖图)` 的派生状态——插件作者只写正向 apply + 隐式逆
471
+ (effect 收集),"何时调用"归框架。
472
+
473
+ 所以更精确的说法不是"只有一个时间维",而是:**"空间维被数据化,时间维被机制化,两者
474
+ 在事件循环上合流。"** 这也正是论文说"时空可组合性"而不说"运行时依赖注入"的原因——
475
+ 它主张的是两维的**乘积**(每次空间组合都可逆),不是单维。
275
476
 
276
- ### 7.1 进程内框架的含义(承接你未完成的 1.5)
477
+ ---
277
478
 
278
- Cordis 是**单进程内**的框架:`Context` 是一个进程内的 proxy,服务解析、事件派发、效应撤销
279
- 全部在内存中。这意味着**一切跨进程/跨机器的组合都必须显式引入 IPC**——Cordis 自己不提供
280
- "远程 context 镜像"(把一个远程进程的服务透明地当作本地 `inject` 到的服务)。
479
+ ## 6. TypeScript 机制答疑:interface / class / export / d.ts
281
480
 
282
- ### 7.2 Recursive Cordis:include 子树 vs alien-binary + IPC 融合
481
+ 读 `lib/types/context.d.ts` 时产生的三个困惑,逐个解开:
283
482
 
284
- 你说的两种方案,可行性截然不同:
483
+ ### 6.1 `interface Context` 与 `class Context` 出现两次——声明合并(Declaration Merging)
285
484
 
286
- **(a) include 子树(直接加载 Dash 插件)——原生、零 IPC、已实现。**
287
- Cordis Multica 作为唯一基底启动 `new Context()`,用 `cordis:include` 把 Dash 的 agent 运行时
288
- 插件直接挂到同一根 context 下(`dsh-app-boot` 的 `mountRootInclude` + dsh 自己 per-session
289
- preset 机制就是同一机制)。Dash 自己的 Cordis 基座**不启动**,Dash 插件直接挂在 Multica 下。
290
- **这就是你说的"直接加载 Dash plugin 就行"——正确,这是默认该走的路。**
485
+ - TS 里 **class 既是值也是类型**:`class Context` 定义了运行时的构造函数+原型(值的一面),
486
+ 同时隐式定义了"实例的形状"(类型的一面)。
487
+ - **同名 interface 会与 class 的实例类型合并**:`export interface Context` 给这个类型追加
488
+ 成员(`events`/`logger`/`root`/symbol 键……)。两个声明不冲突,是**叠加**——这是 TS 的
489
+ 合法特性,不是重复定义。
490
+ - 于是 `Context` 的完整类型 = class 自身成员(`extend/isolate/intercept`)∪ interface
491
+ 写明的四服务属性 ∪ **其他模块后续追加的成员**。
291
492
 
292
- **(b) alien-binary-plugin(启动一个完整 Dash)+ IPC 融合——可行但"融合"是净新增工作。**
293
- "写一个 plugin 去 spawn 一个完整 Dash binary"本身**trivial**:一个 inject `ctx.subprocess` 的
294
- 插件即可。但难点在**"IPC 间的 Cordis 融合"**——把两个 Cordis context 的服务/事件在进程边界
295
- 上桥接起来。**Cordis 没有现成的跨进程 context 桥**:dsh 的跨进程原语(`dsh-sdk-jsonrpc-server`
296
- /sdk-client、`dsh-acp`、`dsh-api-remotes`/api-gateway)暴露的是**一个面**(如 agent 面),不是
297
- **context 本身**(任意服务/事件)。所以:
493
+ "追加"的通道就是你在 `fiber.ts`/`reflect.ts`/`registry.ts`/`events.ts` 开头看到的:
298
494
 
299
- - "大家都是 Cordis,IPC 更容易"在**语义层**成立(共享事件/服务词汇,设计桥时有共同语言);
300
- - 在**机制层**不成立:桥本身是净新增,官方未 ship。
495
+ ```ts
496
+ declare module './context.ts' {
497
+ export interface Context {
498
+ fiber: Fiber // fiber.ts 追加
499
+ get/provide/mixin... // reflect.ts 追加
500
+ plugin/inject... // registry.ts 追加
501
+ on/emit/waterfall... // events.ts 追加
502
+ }
503
+ }
504
+ ```
301
505
 
302
- **判定**:两条路不矛盾。默认走 (a)(零 IPC、原生、已实现);(b) 只在"必须让一个完整 Dash
303
- 独立成进程/独立沙箱/独立升级"时才有意义,且要自建 context-bridge 协议(可复用 sdk/acp/
304
- typert 原语)。当前阶段 (b) 是研究级工作,不是免费午餐。
506
+ 这叫**模块增强(module augmentation)**:每个服务模块把"我投影到 ctx 上的方法"同时写进
507
+ Context 类型。**这与运行时的 mixin/Proxy 机制严格对称**——运行时五个服务的表面被压平成
508
+ 一个 ctx 对象;编译期五个模块的类型表面被合并成一个 Context 接口。所以 Context 的类型是
509
+ "开放命名空间"(谁都能往里加成员),和它的运行时身份(开放命名空间,谁都能 provide)同构。
510
+
511
+ ### 6.2 `.d.ts` 里的 `declare` 与 `export` 给谁
512
+
513
+ - 你读的是 `lib/types/context.d.ts`——**类型声明文件**,构建时由 tsdown/tsc 从 `src/*.ts`
514
+ 生成,随 npm 包发布。`package.json` 的 `types` 字段把 tsc/IDE 引到它。
515
+ - `.d.ts` 里的 `declare` 意思是"**这里只有类型合同,没有实现**"——实现在旁边的
516
+ `lib/*.js` 里。它是给**编译器和 IDE** 的接口文档,不是给引擎的代码。
517
+ - **`export` 的受众不是 Node 引擎,是"导入方"**:其他 TS/JS 模块的 `import` 语句、
518
+ tsc(拿它做类型检查)、IDE(拿它做补全)、bundler(拿它做模块图)。模块系统解决的是
519
+ **模块之间的可见性**(我暴露什么名字给你 import),引擎只是最终执行编译产物。
520
+ - **类型擦除**:`export interface` / `type` / `declare` 这些纯类型导出**编译后不存在**,
521
+ 生成的 JS 里一个字节都不留。只有值导出(`export class Context`)留下运行时实体
522
+ (构造函数+原型)。所以"Context 是不是一个类型?"——**作为类型它是(interface 合并后的
523
+ 开放类型);作为值它也是(class)**;运行时只有后者存在。
524
+ - Node 引擎的角色澄清:引擎(V8)见到的是编译后的 JS(现代 V8 是 JIT 编译执行,不是纯
525
+ 解释,但"先翻译再执行"的直觉可用)。Node 22 确实能直接跑 `.ts`(type-stripping,本仓
526
+ dsh 的 PTC `run_code` 就在用),但那也是**先把类型剥掉再执行**——类型同样不进引擎。
527
+
528
+ ### 6.3 一表总结:同一行代码在四个视角下的身份
529
+
530
+ 以 `export class Context` 为例:
531
+
532
+ | 视角 | 它是什么 |
533
+ |---|---|
534
+ | TS 类型系统 | 值类型(构造函数类型)+ 实例类型(与同名 interface 合并、可被模块增强) |
535
+ | tsc / IDE | 从 `.d.ts` 读合同,检查 import 方的用法 |
536
+ | 编译产物 JS | 一个普通的 class(构造函数 + prototype),Proxy 包装发生在构造函数内部 |
537
+ | 运行时(V8) | 你 `new Context()` 拿到的那个**被 Proxy 包装的对象**——即 §3.2(a) 的根容器 |
305
538
 
306
539
  ---
307
540
 
308
- ## 8. 同类竞品调研
541
+ ## 7. 同类竞品调研
309
542
 
310
543
  真正**同时**达到时间维(运行时保证的逆函数撤销)+ 空间维(响应式依赖自动激活/停用)的,
311
544
  只有 **Cordis、其直系祖先 Koishi、经典对手 OSGi**。
@@ -336,15 +569,15 @@ Umzug 是相邻的时间维-only;bundler 是构建期。**Cordis 的主张在"
336
569
 
337
570
  - `github.com/cordiverse/cordis`(TypeScript/MIT,`packages/{core,create,group,hmr,include,
338
571
  loader,logger-console,timer,utils}`)
339
- - `github.com/cordiverse/paper` —— *A Programming Paradigm for Spatiotemporal Composability*
340
- (Draft 2026-08-13)
572
+ - `github.com/cordiverse/paper` —— *A Programming Paradigm for Spatiotemporal
573
+ Composability*(Draft 2026-08-13)
341
574
  - dsh 仓库 `vendor/README.md`(vendored 清单 + 18 项本地修改 + sync 流程)、
342
- `packages/boot/app-boot/README.md`(bootstrap)、
343
- `.agents/notes/.../2026-07-31-code-runtime-python-fd3-protocol.md` + `py/protocol.py`(fd3)
344
- - Vendored 源码 `~/.dsh/profiles/node_modules/@deepseek-ai/cordis/src/*.ts`(@4.0.1,实测 2693 行)
345
- - Bun 官方 docs(`bun build --compile` / Node-API / Bun.Terminal)、`anthropic.com/news/
346
- anthropic-acquires-bun`、Claude Code quickstart
347
- - `docs.rs/cordis-rs`、`arroyo.dev/blog/rust-plugin-systems`、`extism.org`、`wasmtime`、
348
- `deno_core` / `rquickjs` / `Boa`、`tartanllama.xyz/posts/wasm-plugins`
575
+ `packages/boot/app-boot/README.md`(bootstrap)
576
+ - Vendored 源码一手核验(2026-09-06,本文 §3.2/§4/§5/§6 全部锚点):
577
+ `@deepseek-ai/cordis/src/{context,fiber,service,registry,reflect,events,logger,utils}.ts`
578
+ (@4.0.1,9 文件 2693 行)+ `@deepseek-ai/cordis-plugin-loader/src/`(Loader/Entry/
579
+ EntryGroup/Realm/isolate,dsh vendored)+ `@deepseek-ai/dsh-app-boot/lib/index.js`
580
+ (`cordis:include` 内建挂载)
581
+ - 工程化旁支来源见 `cordis-engineering-feasibility.md`(Bun / fd3 异构桥 / cordis-rs / Extism)
349
582
  - 竞品:OSGi Core Spec 9.0、`github.com/koishijs/koishi`、Effect v3 Scope、VS Code extension
350
583
  anatomy