better-dsh 0.0.0 → 0.2.3

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 (142) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +294 -4
  3. package/control-prompt.md +37 -0
  4. package/cordis.patch.yml +53 -0
  5. package/docs/00_adr/0001-bridge-tool-layer-not-service-layer.md +14 -0
  6. package/docs/00_adr/0002-masking-is-presentation-only.md +15 -0
  7. package/docs/10_plans/A2A-messaging-channel-test-archive.md +256 -0
  8. package/docs/10_plans/code-mode-vs-rlm-ipython-comparison.md +137 -0
  9. package/docs/10_plans/dashr-blueprint-review.md +201 -0
  10. package/docs/10_plans/dashr-blueprint.md +561 -0
  11. package/docs/10_plans/dashr-compaction-window-and-archive.md +307 -0
  12. package/docs/10_plans/dashr-profile-layer-feasibility.md +367 -0
  13. package/docs/10_plans/dashr-sandbox-escalation-semantics-gap.md +171 -0
  14. package/docs/10_plans/dashr-security-sandbox-analysis.md +187 -0
  15. package/docs/10_plans/dashr-surface-invariant-and-omp-imports.md +97 -0
  16. package/docs/10_plans/ipython-kernel-interactive-interface-test-report.md +152 -0
  17. package/docs/10_plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft.md +146 -0
  18. package/docs/10_plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v3.md +50 -0
  19. package/docs/10_plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v4.md +79 -0
  20. package/docs/10_plans/kernel-refactoring/Dash-vs-PrimeAgent-systemprompt-toolcatalog-comparison.md +138 -0
  21. package/docs/10_plans/kernel-refactoring/RLM-system-prompt-injection-gap-report.md +161 -0
  22. package/docs/10_plans/kernel-refactoring/V0.1.5-development-plan.md +109 -0
  23. package/docs/10_plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_dsh.md +50 -0
  24. package/docs/10_plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_prime.md +113 -0
  25. package/docs/10_plans/recallable-compaction.md +147 -0
  26. package/docs/10_plans/spike-tag-repro.mjs +102 -0
  27. package/docs/10_plans/upstream-analysis.md +128 -0
  28. package/docs/50_test-reports/REPL-/345/267/245/345/205/267/350/260/203/347/224/250-/346/210/252/346/226/255/350/257/212/346/226/255.md +110 -0
  29. package/docs/50_test-reports/kernel-provisioning.md +44 -0
  30. package/docs/50_test-reports/repl-kernel-provisioning-test-report.md +87 -0
  31. package/docs/50_test-reports/upstream-dsh-0.1.2-alpha.5-local-test-report.md +81 -0
  32. package/docs/50_test-reports/upstream-dsh-0.1.2-alpha.5-report.md +93 -0
  33. package/docs/50_test-reports/v0.1.8-improved-/345/256/236/346/265/213/346/212/245/345/221/212.md +142 -0
  34. package/docs/50_test-reports/v0.1.8-/345/256/236/346/265/213/346/212/245/345/221/212.md +193 -0
  35. package/docs/50_test-reports/v0.1.8b-/345/256/236/346/265/213/346/212/245/345/221/212.md +96 -0
  36. package/docs/50_test-reports/v0.1.8c-/345/256/236/346/265/213/346/212/245/345/221/212.md +127 -0
  37. package/docs/50_test-reports/v0.1.8d-/345/256/236/346/265/213/346/212/245/345/221/212.md +150 -0
  38. package/docs/50_test-reports/v0.1.8d_artifacts/README.md +138 -0
  39. package/docs/50_test-reports/v0.1.8d_artifacts/code-mode-repl-only.observation.md +74 -0
  40. package/docs/50_test-reports/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.jsonl +3890 -0
  41. package/docs/50_test-reports/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.w-sample-0435.jsonl +544 -0
  42. package/docs/50_test-reports/v0.1.8d_artifacts/functions.json +592 -0
  43. package/docs/50_test-reports/v0.1.8d_artifacts/skills-catalog.snapshot.md +30 -0
  44. package/docs/50_test-reports/v0.1.8d_artifacts/tools-sdk.output-schemas.json +1236 -0
  45. package/docs/50_test-reports/v0.1.8d_artifacts/tools-sdk.python.txt +592 -0
  46. package/docs/50_test-reports/v0.1.8d_artifacts/tools-sdk.typescript.txt +516 -0
  47. package/docs/50_test-reports/v0.1.8d_artifacts/wire-vs-transcription.diff.md +54 -0
  48. package/docs/50_test-reports/v0.1.8e-/345/256/236/346/265/213/346/212/245/345/221/212.md +224 -0
  49. package/docs/50_test-reports/v0.1.9a-/345/256/236/346/265/213/346/212/245/345/221/212.md +168 -0
  50. package/docs/50_test-reports/v0.2.0b-/345/256/236/346/265/213/346/212/245/345/221/212.md +123 -0
  51. package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/Cargo.lock +7 -0
  52. package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/Cargo.toml +6 -0
  53. package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/src/bin/messy.rs +8 -0
  54. package/docs/50_test-reports/v0.2.0b_artifacts/f2probe/src/main.rs +4 -0
  55. package/docs/50_test-reports/v0.2.0b_artifacts/hashline-probe.md +5 -0
  56. package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/Cargo.lock +7 -0
  57. package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/Cargo.toml +7 -0
  58. package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/build.rs +4 -0
  59. package/docs/50_test-reports/v0.2.0b_artifacts/slowprobe/src/main.rs +13 -0
  60. package/docs/50_test-reports/v0.2.1-/345/256/236/346/265/213/346/212/245/345/221/212.md +110 -0
  61. package/docs/50_test-reports/v0.2.1b-/345/256/236/346/265/213/346/212/245/345/221/212.md +86 -0
  62. package/docs/50_test-reports/v0.2.1c-/345/256/236/346/265/213/346/212/245/345/221/212.md +66 -0
  63. package/docs/50_test-reports/v0.2.1d-/345/256/236/346/265/213/346/212/245/345/221/212.md +67 -0
  64. package/docs/50_test-reports/v0.2.1e-P1-/345/256/236/346/265/213/346/212/245/345/221/212.md +136 -0
  65. package/docs/50_test-reports/v0.2.1ef-dev-audit-report.md +73 -0
  66. package/docs/50_test-reports/v0.2.1f-plugin-shipped-ui-patches/345/256/236/346/265/213/346/212/245/345/221/212.md +102 -0
  67. package/docs/60_exploration-and-research/cordis-research.md +350 -0
  68. package/docs/60_exploration-and-research/dsh-web-profile-package-map.md +186 -0
  69. package/docs/60_exploration-and-research/dsh-web-ui-slot-system-research.md +310 -0
  70. package/docs/60_exploration-and-research/dsh-webui-strip-boundary-research.md +300 -0
  71. package/docs/60_exploration-and-research/ios-chat-app-bridge-research.md +324 -0
  72. package/docs/60_exploration-and-research/web-frontend-composability-research.md +191 -0
  73. package/docs/REPL-/345/267/245/345/205/267/350/260/203/347/224/250-/346/210/252/346/226/255/350/257/212/346/226/255.md +110 -0
  74. package/docs/adr/0001-bridge-tool-layer-not-service-layer.md +14 -0
  75. package/docs/adr/0002-masking-is-presentation-only.md +15 -0
  76. package/docs/distro-blueprint.md +81 -0
  77. package/docs/dsh-webUI-with-rlm-mode.png +0 -0
  78. package/docs/plans/A2A-messaging-channel-test-archive.md +256 -0
  79. package/docs/plans/code-mode-vs-rlm-ipython-comparison.md +137 -0
  80. package/docs/plans/dashr-blueprint-review.md +201 -0
  81. package/docs/plans/dashr-blueprint.md +561 -0
  82. package/docs/plans/dashr-compaction-window-and-archive.md +307 -0
  83. package/docs/plans/dashr-profile-layer-feasibility.md +367 -0
  84. package/docs/plans/dashr-sandbox-escalation-semantics-gap.md +171 -0
  85. package/docs/plans/dashr-security-sandbox-analysis.md +187 -0
  86. package/docs/plans/dashr-surface-invariant-and-omp-imports.md +97 -0
  87. package/docs/plans/ipython-kernel-interactive-interface-test-report.md +152 -0
  88. package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft.md +146 -0
  89. package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v3.md +50 -0
  90. package/docs/plans/kernel-refactoring/Dash-IPython-Control-Prompt-draft_v4.md +79 -0
  91. package/docs/plans/kernel-refactoring/Dash-vs-PrimeAgent-systemprompt-toolcatalog-comparison.md +138 -0
  92. package/docs/plans/kernel-refactoring/RLM-system-prompt-injection-gap-report.md +161 -0
  93. package/docs/plans/kernel-refactoring/V0.1.5-development-plan.md +109 -0
  94. package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_dsh.md +50 -0
  95. package/docs/plans/kernel-refactoring/actinoable-surface-to-llm-in-agent-runtime_prime.md +113 -0
  96. package/docs/plans/recallable-compaction.md +147 -0
  97. package/docs/plans/spike-tag-repro.mjs +102 -0
  98. package/docs/plans/upstream-analysis.md +128 -0
  99. package/docs/repositioning-and-rebranding.md +102 -0
  100. package/docs/v0.1.8-improved-/345/256/236/346/265/213/346/212/245/345/221/212.md +142 -0
  101. package/docs/v0.1.8-/345/256/236/346/265/213/346/212/245/345/221/212.md +193 -0
  102. package/docs/v0.1.8b-/345/256/236/346/265/213/346/212/245/345/221/212.md +96 -0
  103. package/docs/v0.1.8c-/345/256/236/346/265/213/346/212/245/345/221/212.md +127 -0
  104. package/docs/v0.1.8d-/345/256/236/346/265/213/346/212/245/345/221/212.md +150 -0
  105. package/docs/v0.1.8d_artifacts/README.md +138 -0
  106. package/docs/v0.1.8d_artifacts/code-mode-repl-only.observation.md +74 -0
  107. package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.jsonl +3890 -0
  108. package/docs/v0.1.8d_artifacts/dsh-session-session-4a293388-9ae1-474b-87a0-9e17bb556d94.w-sample-0435.jsonl +544 -0
  109. package/docs/v0.1.8d_artifacts/functions.json +592 -0
  110. package/docs/v0.1.8d_artifacts/skills-catalog.snapshot.md +30 -0
  111. package/docs/v0.1.8d_artifacts/tools-sdk.output-schemas.json +1236 -0
  112. package/docs/v0.1.8d_artifacts/tools-sdk.python.txt +592 -0
  113. package/docs/v0.1.8d_artifacts/tools-sdk.typescript.txt +516 -0
  114. package/docs/v0.1.8d_artifacts/wire-vs-transcription.diff.md +54 -0
  115. package/docs/v0.1.8e-/345/256/236/346/265/213/346/212/245/345/221/212.md +224 -0
  116. package/docs/v0.1.9a-/345/256/236/346/265/213/346/212/245/345/221/212.md +168 -0
  117. package/docs/v0.2.0b-/345/256/236/346/265/213/346/212/245/345/221/212.md +123 -0
  118. package/docs/v0.2.0b_artifacts/f2probe/Cargo.lock +7 -0
  119. package/docs/v0.2.0b_artifacts/f2probe/Cargo.toml +6 -0
  120. package/docs/v0.2.0b_artifacts/f2probe/src/bin/messy.rs +8 -0
  121. package/docs/v0.2.0b_artifacts/f2probe/src/main.rs +4 -0
  122. package/docs/v0.2.0b_artifacts/hashline-probe.md +5 -0
  123. package/docs/v0.2.0b_artifacts/slowprobe/Cargo.lock +7 -0
  124. package/docs/v0.2.0b_artifacts/slowprobe/Cargo.toml +7 -0
  125. package/docs/v0.2.0b_artifacts/slowprobe/build.rs +4 -0
  126. package/docs/v0.2.0b_artifacts/slowprobe/src/main.rs +13 -0
  127. package/docs/v0.2.1-/345/256/236/346/265/213/346/212/245/345/221/212.md +110 -0
  128. package/docs/v0.2.1b-/345/256/236/346/265/213/346/212/245/345/221/212.md +86 -0
  129. package/docs/v0.2.1c-/345/256/236/346/265/213/346/212/245/345/221/212.md +66 -0
  130. package/lib/client/index.js +473 -0
  131. package/lib/index.d.ts +736 -0
  132. package/lib/index.js +11518 -0
  133. package/lib/kernel-env-hxaihi9C.js +195 -0
  134. package/lib/kernel-env.d.ts +80 -0
  135. package/lib/kernel-env.js +3 -0
  136. package/lib/py-sdk-BCaOGYz7.d.ts +125 -0
  137. package/lib/py-sdk-CbgYiX8O.js +691 -0
  138. package/lib/py-sdk.d.ts +2 -0
  139. package/lib/py-sdk.js +3 -0
  140. package/package.json +325 -4
  141. package/scripts/kernel-provision.mjs +35 -0
  142. package/index.js +0 -3
@@ -0,0 +1,102 @@
1
+ # v0.2.1f 实测报告 — plugin-shipped-ui-patches(web-trust-fence + mobile-layout)
2
+
3
+ > 2026-09-03 · change `2026-09-03-plugin-shipped-ui-patches` · 4999 源码级实例(`dsh-v0.1.2-alpha.5` + better-dsh 0.2.1-f)
4
+
5
+ ## 0. 一句话结论
6
+
7
+ 两个原"user 私人手改 patch"以插件随发形态落地:**P2 = bundle patch 行覆盖(fence 腿)+ webserver/index-inject boot script(isLoopback 腿);P3 = client 半 mobile 模块(CSS + 三条件手势)**。全部机检绿(423/423 + tsc 0),4999 双腿实证通过;浏览器/真机两级确认留给 user。
8
+
9
+ ## 1. 实测范围与结果
10
+
11
+ | # | 项 | 结果 |
12
+ |---|---|---|
13
+ | T1 | vitest 全量(403 基线 + web-trust 8 + mobile-gesture 12) | ✅ **423/423** |
14
+ | T2 | tsc --noEmit | ✅ 0 错 |
15
+ | T3 | `!!js` 表达式:真实 js-yaml 方言解析 + loader 语义 evaluate | ✅ env 合并 `["a.example","b.example","192.168.31.130","lan"]`;空 env = 原生等价 |
16
+ | T4 | dump-config 合成 | ✅ connection 行标注 `# == @deepseek-ai/dsh-web-app, patched by @pgmi-builds/better-dsh`(层序 + 出处) |
17
+ | T5 | **fence 腿活体**(daemon 带 `DSH_TRUSTED_HOSTS=probe.example`) | ✅ `Host: probe.example` → **401**(过栅栏、卡认证 = 过关签名);`Host: evil.example` → **403** |
18
+ | T6 | **isLoopback 腿活体**(boot script 注入) | ✅ shell HTML 含 `(function(){try{if(!window.__DSH_TRANSPORT__){var h=location.hostname,a=["probe.example","192.168.31.130"];…` —— 防覆盖守卫 + JSON 嵌入 authorities 全在场 |
19
+ | T7 | **mobile 全局** | ✅ 同页 `__DASHR_MOBILE__={"enabled":true,"breakpoint":1024,"swipeDistancePx":48,"swipeVelocityPxPerMs":0.35,"edgeBandPx":28}` |
20
+ | T8 | **client bundle mobile 模块** | ✅ application batch(3.7MB)含 `data-sidebar-collapsed`×2 / `__DASHR_MOBILE__`×2 / `toggleSidebar`×10 |
21
+ | T9 | boot graph | ✅ `@pgmi-builds/better-dsh` 行在场(沿 v0.2.1e 既验路径) |
22
+
23
+ ## 2. 实现面(代码清单)
24
+
25
+ - `dashr/cordis.patch.yml`:+`connection` 行覆盖(整行重述 + `trustedHosts: !!js (env).split(/\s+/).filter(Boolean).concat(ctx.webRuntime.trustedHosts)`;⚠ `!!js` 是 scalar tag,表达式不能以 `[` 开头——flow-seq 会被 yaml 拒)。
26
+ - `dashr/src/web-trust.ts`(新,host 半):`buildBootScript` 纯函数 + `installWebTrust`(`webserver/index-inject` 监听,emit 时新鲜读 config)。一个内联 head script 双效:trusted hostname → `window.__DSH_TRANSPORT__={ownsHost:true}`(无 fetch/openStream,唯一效应 = isLoopback 翻真;不覆盖已有 transport);mobile 阈值 → `window.__DASHR_MOBILE__`。
27
+ - `dashr/src/mobile/gesture.ts`(新,纯):`isRecognizedSwipe`(起点∧距离∧速率 + 横向占优 + touch/pen only)、`isInteractiveOrigin`、`isNarrowViewport`、`resolveMobileConfig`。
28
+ - `dashr/src/mobile/client/index.ts`(新,client 半):`setupMobileLayout`——`<style data-plugin data-plugin-css>` 注入(媒体查询 + `[data-sidebar-collapsed]` + `!important` 覆盖内联 grid template → 侧栏轨 0)+ document 级 pointer 监听 → `ctx.layout.toggleSidebar()`(窄视口=翻 narrowExpanded)。
29
+ - `dashr/src/index.ts`:Config + `trustedPageAuthorities` / `mobile`(schema 默认 enabled);apply 挂 `installWebTrust`。
30
+ - `dashr/src/failover/client/index.ts`:client 入口挂 `setupMobileLayout`;package.json `dsh.client.inject` + `@deepseek-ai/dsh-client-ui-layout`(模块表边)。
31
+ - tsconfig exclude + `src/mobile/client`(沿 failover/client 的 client 半约定)。
32
+
33
+ ## 3. 病因与定性回执(P2 复诊闭环)
34
+
35
+ - prod 消息 `…failed: settings are unavailable in this browser` = 浏览器侧 `isLoopback` 门(`ui-settings` memory 持久化终态不可用),非服务端 fence。
36
+ - 上游定性(设计笔记实证):有意 = "非 loopback 关 Host 持久化(偏好进程本地、页面照常)";无人设计 = mirror 把 memory 做成终态不可用 + trustedHosts 未传导到 host facts——当年源码 patch 存在的全部理由。
37
+ - B 机制恢复的正是设计意图形态(remote 页面照常 + Host 持久化)。
38
+
39
+ ## 4. 验证环境事实
40
+
41
+ - 4999:systemd-run `dsh-4999-test`,`DSH_TRUSTED_HOSTS=probe.example`;profile patch 覆盖 `dashr-repl` 行 `trustedPageAuthorities: ['probe.example','192.168.31.130']`(+ 重述 python 表达式)。
42
+ - monorepo 副本重建:`pnpm --filter @pgmi-builds/better-dsh exec tsdown` + `pnpm exec tsx scripts/build-client.ts`(34ms)。
43
+ - 旋钮惰性双态:无 env / 无 authorities 时 fence 与 boot script 均与原生一致(T3 + `buildBootScript` 空态 undefined 单测)。
44
+
45
+ ## 5. 待 user 确认(browser/真机级)
46
+
47
+ - [ ] 手机经 `http://192.168.31.130:4999`(或 prod 域名):Settings > Models 清单加载、settings 编辑持久化生效(isLoopback 腿端到端)。
48
+ - [ ] 手机窄视口:侧栏隐藏(轨 0)、快速轻扫开/关 overlay、慢速拖选文本不触发(三条件手势端到端)。
49
+ - [ ] 桌面 ≥1024 视口不受影响;平板窄档(920–1023,details 开)表现复核。
50
+
51
+ ## 6. 遗留
52
+
53
+ - prod 部署升级(alpha.5+ + 0.2.1-f + `trustedPageAuthorities`/`DSH_TRUSTED_HOSTS` 配置 + retired 手改 patch 清单)——按年龄门精确版本流程另走。
54
+ - `ownsHost` off-label 风险与 connection 行形状漂移:进 upstream-alignment S7 查表(已加两项)。
55
+
56
+ ## 7. Round 2(2026-09-03 user 实测反馈闭环)
57
+
58
+ - **阈值**:速率默认 0.35 → **0.2 px/ms**("要求太快"裁定)。
59
+ - **左滑关闭从不触发的根因**:不是速度——首版 origin 条件只认屏幕边缘带,而关闭手势的手指落在展开的侧栏本体上,全被拒。round 2 改为面板状态感知分类器 `classifySwipe`(展开态任意起点左滑即关)。
60
+ - **新增右栏手势**:右缘带左滑 `openDetails`、非左缘右滑 `closeDetails`(面板状态读 AppFrame 语义属性)。
61
+ - **4999 手机通道**:`--host 0.0.0.0` 被启动器故意拒绝("would expose remote code execution"——上游安全立场,与 prod 的 Caddy 反代拓扑呼应);改设 socat 测试反代 `dsh-4999-lan`(0.0.0.0:4990 → 127.0.0.1:4999),手机经 `http://192.168.31.130:4990/?token=…` 直测,fence(port-less 192.168.31.130 ∈ DSH_TRUSTED_HOSTS)与 isLoopback(∈ trustedPageAuthorities)双腿在此路径生效(LAN 直连 401=fence 过 + shell/boot script/RPC 200 已验)。
62
+ - **Models 页复诊**:三条 RPC(llm/listProviders、llm/listConfigurableProviders、settings/describe)loopback 直连全 ok——4999 后端无恙;user 手机所见的 Models 失败实为 prod(v0.2.1f 未部署)。
63
+ - **dsh-better-sidebar**:0.17.1 与 alpha.5 不兼容(import 的 `settingsNamespace` 已被上游 settings 面重设计移除);装 **0.18.0-alpha.0** 通过(boot graph 已含其 client.js);profile pnpm-workspace allowBuilds 补 `node-pty: true` 并 rebuild。
64
+
65
+ ## 8. Round 3(2026-09-03 前端机制实证 + sidecarx 对齐)
66
+
67
+ - **机制链真浏览器实证**(CDP 驱动 google-chrome headless,经手机同款拼写 `192.168.31.130:4990`):`window.__DSH_TRANSPORT__ = {"ownsHost":true}` 翻真;Settings > Models 页渲染出目录("Enter your API keys… DeepSeek / Edit / Add a custom provider");截图 `.scratch/models-page-proof.png`。脚本位次(HTML 实测):queue script(100) → … → `__DSH_BOOT__`(3637) → **boot script(23778)** → shell 主入口(25168)——先于一切 application bundle 物化。
68
+ - **sidecarx 对齐**(user 参照 `sidecarx/src/webclient/src`):参照实现**无速率门槛**(`app.js:332` `SWIPE_THRESHOLD=50` + 左缘带 40px + 展开态任意起点左滑关 + `absDY>absDX` 拒绝;overlay-viewer 同款数学)——默认改为 sidecarx 同款:distance 50 / edgeBand 40 / **velocity 0(禁用)**;三条件模式保留为配置旋钮(armed 时生效,慢速拖选 ~0.13 px/ms 为标定参考)。429/429 绿。
69
+ - 前端机制一句话:**变量是前端运行时的(`window.__DSH_TRANSPORT__`,connection 浏览器半 apply 时读取),载体是服务端渲染的页面 HTML**(host 插件经公共事件 `webserver/index-inject` 注入的内联 head script)——这是插件触达 bundle 加载前时序的唯一途径。
70
+
71
+ ## Round 4(2026-09-03)— 手势整体重写:z_dsh-alpha `1706b81` 逐条照搬 + 速率门
72
+
73
+ **触发**:user 指出 round-2 报告的 48/28/0.2 三元组无任何源码出处(系发明值),要求以旧 ui-layout 补丁(退役目录 `z_dsh-alpha`,commit `1706b81` "ui-layout: mobile swipe opens Better Sidebar right panel via its DOM toggle")为 **exact** 基线,唯一增量 = 速率门控。
74
+
75
+ **根因链(本轮三个症状全部落地)**:
76
+ 1. "必须贴着左边缘" — round-2/3 缘带 28/40px;源码是 **X120**(`e.clientX > 120` 才拒绝),右侧起点区为**右 3/4 屏**(`x < viewport/4` 才惰性)。
77
+ 2. "还是要划很快" — 部署物里速率确实是 0(boot script 实证);真因是 round-3 在 **pointerup** 才判定,慢划先被浏览器滚动启发式 pointercancel 吃掉。源码在 **pointermove 中途**(|dx|≥40 ∧ |dx|>|dy|×1.3 达成即触发,一次性)。
78
+ 3. "右侧栏怎么划都出不来" — round-3 调的是上游 details 面板(`ctx.layout.openDetails()`);用户的右栏是 **Better Sidebar 浮层**,源码经 `[data-dsh-toggle-cluster]` 末按钮 DOM click 切换。上游 details 在窄视口被强制关闭,永远不可见。
79
+
80
+ **落地**(commit `0676811`):`gesture.ts`/`mobile/client/index.ts` 全重写为源码语义;断点 <768(SIDEBAR_MOBILE);唯一增量速率门默认 0.15 px/ms(慢速文本拖拽 ~0.13 被滤,舒适滑扫 ≥0.4 通过;0=关闭)。`test.pc.randomhash.app` 加入 `trustedPageAuthorities`(Caddy 反代该域 → 127.0.0.1:4999,Host 被改写为 loopback 过 fence,但页面 origin 是该域名——此前不在信任列表,isLoopback=false → Models 挂)。
81
+
82
+ **实证**(CDP 真浏览器,用户真实 URL `https://test.pc.randomhash.app`):
83
+ - 手机模拟 390×844 触摸序列:开左栏 ✓ / 关左栏 ✓ / 开右栏(Better Sidebar)✓ / 关右栏 ✓;`__DSH_TRANSPORT={ownsHost:true}`、`__DASHR_MOBILE` 七键全对。
84
+ - 桌面视口:Settings → Models 渲染 provider 列表(DeepSeek / Edit / Add provider)✓。
85
+ - 服务端:boot script 含新阈值与 `test.pc.randomhash.app`;client bundle rev `e9e06225516c` 含全部新标记。
86
+ - 单测 425/425,tsc 0。远程(Hermes/OMP a2a)独立浏览器验证已派出。
87
+
88
+ **遗留**:真机(手机/MacBook)复测;远程 agent 回报收取。
89
+
90
+ ## Round 4 收口(2026-09-03)— 真机确认
91
+
92
+ user 于 round-4 部署后在手机/MacBook 经 `https://test.pc.randomhash.app` 复测,回复 **"UI ok"**——Models 页 + 移动手势(X120 左缘带 / 右 ¾ 起点区 / move 触发 / Better Sidebar 右栏 DOM toggle / 速率门 0.15)真机验收通过。round-4 遗留仅剩远程 agent(Hermes/OMP)独立回报收取(a2a 已派出)。
93
+
94
+ ## 附:hashline edit 升级透传 bug(2026-09-03,commit `e27fdec`)
95
+
96
+ **症状**(user 报告):4999 会话内 agent 带齐 `sandbox_permissions:"danger-full-access"` + `justification` 重试 edit,审批卡永不弹,循环收 `[sandbox: file access denied under workspace-write mode]` + escalation-available 提示。
97
+
98
+ **证据**(session `8e966430`,zstd session log):8 次升级 edit 全部 plain 拒绝,全程 `approval/asked` 事件数 = 0;同 session 的 `write` 升级正常弹卡并被 `allowed-once`。上游 `approveEscalation` 是 fail-closed 设计——升级参数若真触达,要么弹卡要么抛专用错误文本,绝不会返回 plain 拒绝+提示。
99
+
100
+ **根因**:better-dsh vendored hashline `tool-edit.js` 在 `sandbox.resolvePolicy("edit", {path, edits}, exec)` 调用点手工重构对象,丢弃了 `sandbox_permissions`/`justification`(讽刺的是 `canonical` 里就带着——`contract.normalizeRequest` 显式回填)。`resolvePolicy` 见不到字段 → 永远返回 standing policy → 围栏拒绝。`batch_edit`/`undo_last_edit` 传 `canonical`,无此问题;`write`/`bash` 走上游 tool-fs/tool-bash 传全参,无此问题。
101
+
102
+ **修复**:调用点透传两字段;回归测试 `test/url-schema/escalation-forward.spec.ts`(stub sandbox 捕获 resolvePolicy 入参 + 预中止 signal 隔离 fs 半边)2 例;427/427 + tsc 0;已部署 4999。**注意 prod(3080,npm 0.2.1-d)同样带此 bug**,随下个发布版本收敛。
@@ -0,0 +1,350 @@
1
+ # Cordis 框架研究
2
+
3
+ > 记录:2026-08-21 · 一手核验:vendored `@deepseek-ai/cordis` 4.x(本地 4.0.1)源码 +
4
+ > `github.com/cordiverse/cordis`(6770★)+ `github.com/cordiverse/paper`(2532★)+
5
+ > dsh 官方 docs(`master` @ 2026-08-19)+ dsh `vendor/README.md`。
6
+ > 本文是 Cordis 单侧研究(与 Dash 解耦后的文档;Dash 侧见 `dash-research.md`)。
7
+
8
+ ---
9
+
10
+ ## 1. 总览与时空可组合性
11
+
12
+ ### 一句话
13
+
14
+ **Cordis 是"时空可组合性的元框架"(A Meta-Framework of Spatiotemporal Composability)**:
15
+ 一个 TypeScript 插件框架,把经典类型论的 *effect/coeffect* 对偶落成两套运行时机制——
16
+ **可逆效应(Revertible Effects,时间维)** 与 **反应式余效应(Reactive Coeffects,空间维)**,
17
+ 使插件"插得上也拔得下、依赖变化自动激活/停用",全程免重启进程。
18
+
19
+ ### 血统与时间线
20
+
21
+ - 作者 **Shigma(施一凡)**,Koishi 聊天机器人框架作者(2020-01 首版)。2023 年已写
22
+ 《可逆的插件系统》设计文(koishi.chat cookbook)——论文雏形,非学术论文。
23
+ - **2026-08-13**:论文 + dsh 开源同日。论文 *A Programming Paradigm for Spatiotemporal
24
+ Composability*,DeepSeek × 北大,88 页,`github.com/cordiverse/paper`("Draft of August
25
+ 13, 2026",active revision)。
26
+ - `Cordis` = 拉丁语"心";Koishi 的一切都从 Cordis 开始。全程先工程后理论,DeepSeek 参与
27
+ 是 2026 年的事。
28
+
29
+ ### 理论支柱:effect / coeffect 对偶 → 运行时机制
30
+
31
+ | 维度 | 论文概念 | 工程语义 | 解决的问题 |
32
+ |---|---|---|---|
33
+ | **时间维** | **Revertible Effects** | 每次上下文修改配显式逆函数,叠成撤销链;卸载时反向执行 | 插件"插得上拔不下"(状态残留、伤及运行中组件) |
34
+ | **空间维** | **Reactive Coeffects** | 组件声明依赖 → 自动拓扑编排;依赖齐→ACTIVE,缺→INACTIVE;提供者撤走→依赖者先停;回归→自动恢复 | 补丁式依赖维护 / 循环依赖 / 手写编排代码 |
35
+
36
+ 并统一 effect context 与 coeffect context 为**单一 context 类型**(这即"编程范式"),再组合成
37
+ **component(组件)**,给出**动态组合的演算**,其元理论把时空可组合性从单组件推广到整个
38
+ 交错组件系统。
39
+
40
+ ### 作为代码的实现(源码级取证)
41
+
42
+ 核心包 9 个源文件(`context/service/fiber/events/registry/reflect/logger/utils/index`,
43
+ **实测 2693 行**)。四个构件落地时空可组合性:
44
+
45
+ - **`Context`** = 运行时代理(proxy):属性读取走服务解析器(`ctx.tools`/`ctx.llm` 由服务名
46
+ 解析,非 import 具体实现)。`extend()`/`isolate()`/`intercept()` 创建作用域子 context。
47
+ - **`Fiber`** = 一个插件应用的运行时。持有 `_disposables`(DisposableList)、`inject`(依赖
48
+ 声明)、`store`(已解析实现)、`state`(FiberState)、`epoch`(依赖满足度签名)。
49
+ - **时间维** `Fiber.effect(execute, label)`:`execute` 立即执行,产出 disposer 被收集;
50
+ disposer 被调 **或** fiber 卸载时,disposers **逆注册序(LIFO)** 执行(
51
+ `disposables.splice(0).reverse()`)。
52
+ - **空间维** `inject` + `_checkImpl` + `_refresh` + `epoch`:遍历每个依赖,任一缺失 →
53
+ `epoch=INACTIVE` → `_unload()`(停用);全部存在 → `epoch=':uid:uid...'` → `_reload()`
54
+ (激活);**提供者 fiber uid 变化** → epoch 变化 → 依赖者先 unload 再 reload。
55
+ - **`Service`** = 服务基类:构造函数 `super(ctx, name)` 即 `ctx.reflect.provide(...)` 注册,
56
+ 随所属 fiber 卸载自动注销。
57
+ - **`EventsService`** = 事件总线,五种派发模式 `emit/parallel/serial/bail/waterfall`
58
+ (waterfall 是 around-middleware,`next()` 委派、不调则短路)。
59
+
60
+ ### 生产验证与自认局限
61
+
62
+ Koishi:4 年、4000+ 社区插件、作者互不相识,唯一协调机制 = 反应式余效应;切存储后端 /
63
+ 重连 IM 适配器时仅依赖变化的插件重激活。论文自认局限:仅 Koishi 一生态验证、仅 TS 单语言
64
+ 数据、无与其他替代架构的直接对比。
65
+
66
+ ---
67
+
68
+ ## 2. 非特权核心(No Privileged Core)
69
+
70
+ ### 你的理解的校验(逐点)
71
+
72
+ 你的 mental model 基本正确,有两处需要精确化:
73
+
74
+ > "基底 binary 可以看作一个入口"
75
+
76
+ ✅ 精确。`dsh` 这个 binary 是 `apps/cli` 产出的 `lib/bin.js`,官方自述为 **"a thin
77
+ self-executing composition"**(薄的自执行组合)——它只做一件事:调 `dsh-app-boot` 的
78
+ `boot()`。
79
+
80
+ > "核心自己自举(bootstrap)为一个 plugin"
81
+
82
+ ⚠️ 半对。**框架基底(Context + reflect/registry/events/logger 四服务)不是 plugin,它就是
83
+ 框架本身**——不是"自举成 plugin",而是"进程里那个被 `new Context()` 造出来的根容器"。
84
+ 真正被"自举/挂载"的是 **App 的插件树**(dsh 的 ~195 个插件),它们经 Loader 从
85
+ `dsh-base` 的 `cordis.patch.yml` 逐行 insert 到根 context 上。
86
+
87
+ > "如果你基于 Cordis 写了另一个 plugin,让它自举为一个核心也是可以的"
88
+
89
+ ✅ 对,且措辞可更准:你不是"写一个 plugin 当核心",而是**写一个 Cordis App(一个插件树 +
90
+ 一个薄 bin)**,它与 Dash 地位完全平等。Dash 只是"基于 Cordis 的 App 特例"。
91
+
92
+ > "Dash 第一个自举的 plugin 就成了核心"
93
+
94
+ ⚠️ 需要修正。**没有"第一个插件成为核心"这回事**。"no privileged core" 的准确含义是:
95
+ **框架之外没有任何硬编码的、不可替换的模块**。看起来像"核心"的东西(`session`/
96
+ `system-prompt`/`tools`/`agent`/`agent-loop` 这六件"脊柱")**全是 `dsh-base` 的
97
+ `cordis.patch.yml` 里的普通插件行**,可被上层 patch 按 id 整行替换。所谓"核心"只是
98
+ "恰好提供了别人 inject 的基础服务的那个插件",并非特权位。
99
+
100
+ ### Bootstrap 链(源码取证)
101
+
102
+ ```
103
+ dsh 二进制(lib/bin.js,薄壳)
104
+ └─ boot(binName, configPath, ...) # dsh-app-boot
105
+ ├─ new Context() # 根容器(框架基底,非 plugin)
106
+ ├─ 注册 cordis:include + cordis:group builtins
107
+ ├─ 安装 Loader
108
+ ├─ mountRootInclude(...) # 挂载 cordis.yml 配置树
109
+ │ └─ 逐层叠加:bundle patch 层 → profile patch → home patch → --patch
110
+ │ └─ 每行 = 一个 plugin(含 dsh-base 的 agent-loop 等"核心")
111
+ ├─ assertEntriesLoaded / Activated # 全部解析/激活,否则 fail loud
112
+ └─ 返回根 Context
113
+ ```
114
+
115
+ **关键证据**:`dsh-app-boot` 的 `boot()` 注释明言 "Create the root context, install Loader,
116
+ ... mount and await the include tree ... return the root context"。核心(框架)与 App
117
+ (插件树)的边界就在 `new Context()` 这一行:它之上全是可 patch 的插件,它本身才是唯一的
118
+ "内核"。
119
+
120
+ ---
121
+
122
+ ## 3. 架构结构测绘
123
+
124
+ ### Monorepo 包结构(`cordiverse/cordis`,9 包)
125
+
126
+ ```
127
+ cordis/
128
+ └── packages/
129
+ ├── core/ → 发布为 `cordis`:Context/Fiber/Service/Events/Registry/Reflect/Logger
130
+ ├── loader/ → `@cordisjs/plugin-loader`:声明式配置加载(YAML/JSON 行 → 插件树)
131
+ ├── include/ → `@cordisjs/plugin-include`:配置子树挂载 + `!!js` 表达式
132
+ ├── hmr/ → `@cordisjs/plugin-hmr`:热模块替换(保存 → 仅重应用该插件)
133
+ ├── group/ → `@cordisjs/plugin-group`:插件分组(isolate realm)
134
+ ├── timer/ → `@cordisjs/plugin-timer`:定时服务
135
+ ├── create/ → 脚手架(Node 22+)
136
+ ├── logger-console/ → 控制台日志后端
137
+ └── utils/ → 共享工具
138
+ ```
139
+
140
+ ### 核心组件与关系
141
+
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
+ ```
157
+
158
+ ---
159
+
160
+ ## 4. 开发语言与打包编译
161
+
162
+ ### 4.1 进程内约束(TS-only,Node ESM)
163
+
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 格式)。
170
+
171
+ ### 4.2 打包编译引擎:能否换成 Bun
172
+
173
+ **结论:Cordis 核心(零 native 依赖)可 trivial 编译成 Bun 单文件可执行;dsh 全量表面
174
+ "可行但有工程成本",不是"被卡死"。Claude Code 就是现成先例。**
175
+
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。
193
+
194
+ ---
195
+
196
+ ## 5. 异构代码衔接层(Hetero-Language Bridge)
197
+
198
+ ### 结论:官方**已经实现**了"异构语言成为插件模块",形式 = 进程边界 + IPC 桥
199
+
200
+ 你对"其实是不是 subprocess 倒无所谓,只要逻辑层自洽、实现无感"的判断是对的——Cordis 的
201
+ "everything is plugin" 抽象**确实自洽**:从 Cordis 视角看,`PythonCodeRuntime` 就是一个
202
+ 普通 Service provider;它内部 spawn 一个 Python 子进程、用 fd3 桥接,是藏在 `ctx.codeRuntime`
203
+ 缝隙背后的实现细节,消费者无感。
204
+
205
+ ### 官方实现(`dsh-code-runtime-python` 的 fd3 帧协议)
206
+
207
+ 仓库里**真实实现**(非 stub):`packages/code-runtime/code-runtime-python/`(`src/index.ts`
208
+ + `py/protocol.py` + e2e tests)。机制:
209
+
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 三次字段漂移而加此守卫)。
221
+
222
+ TS 侧 `src/index.ts` 把 `PythonCodeRuntime` 注册为 `ctx.codeRuntime` 的 provider——**这就是
223
+ "Python 成为 Cordis 插件"的官方答案:TS 宿主侧插件 + 外语子进程 + fd3 JSON-lines IPC**。
224
+
225
+ ### 全部异构缝隙(进程边界是唯一的跨语言方式)
226
+
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 |
235
+
236
+ > 注意:默认 `web` profile 只 bundle `dsh-code-runtime-worker-thread`(TS 后端);Python 后端
237
+ > 在仓库里但不随默认 profile 发布,需显式安装。
238
+
239
+ ---
240
+
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 |
266
+
267
+ **诚实成本**:原生核心 8–15k LOC / 4–8 周达到对等;**JS 互操作是真正耗时数月的硬层**
268
+ (要么在嵌入式引擎里重实现 Node API 面——无先例,`napi-rs` 是反方向;要么让响应式
269
+ inject-epoch 语义跨进程边界保持一致)。所以正确的心智模型不是"Rust 版 Cordis 万能兼容 TS",
270
+ 而是 **原生 Rust 核心 + TS 插件作为又一种异构语言类型(出进程 Node/Bun + 桥)**。
271
+
272
+ ---
273
+
274
+ ## 7. 进程内框架与 Recursive Cordis
275
+
276
+ ### 7.1 进程内框架的含义(承接你未完成的 1.5)
277
+
278
+ Cordis 是**单进程内**的框架:`Context` 是一个进程内的 proxy,服务解析、事件派发、效应撤销
279
+ 全部在内存中。这意味着**一切跨进程/跨机器的组合都必须显式引入 IPC**——Cordis 自己不提供
280
+ "远程 context 镜像"(把一个远程进程的服务透明地当作本地 `inject` 到的服务)。
281
+
282
+ ### 7.2 Recursive Cordis:include 子树 vs alien-binary + IPC 融合
283
+
284
+ 你说的两种方案,可行性截然不同:
285
+
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 就行"——正确,这是默认该走的路。**
291
+
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 本身**(任意服务/事件)。所以:
298
+
299
+ - "大家都是 Cordis,IPC 更容易"在**语义层**成立(共享事件/服务词汇,设计桥时有共同语言);
300
+ - 在**机制层**不成立:桥本身是净新增,官方未 ship。
301
+
302
+ **判定**:两条路不矛盾。默认走 (a)(零 IPC、原生、已实现);(b) 只在"必须让一个完整 Dash
303
+ 独立成进程/独立沙箱/独立升级"时才有意义,且要自建 context-bridge 协议(可复用 sdk/acp/
304
+ typert 原语)。当前阶段 (b) 是研究级工作,不是免费午餐。
305
+
306
+ ---
307
+
308
+ ## 8. 同类竞品调研
309
+
310
+ 真正**同时**达到时间维(运行时保证的逆函数撤销)+ 空间维(响应式依赖自动激活/停用)的,
311
+ 只有 **Cordis、其直系祖先 Koishi、经典对手 OSGi**。
312
+
313
+ | 框架 | 语言 | 时间维 | 空间维 | 免重启卸载 |
314
+ |---|---|---|---|---|
315
+ | **Cordis** | TS | ✅ `Fiber.effect()` LIFO 撤销 | ✅ `inject`+epoch | ✅ |
316
+ | **Koishi** | TS | ✅ `fork.dispose()` 逆序撤销 | ✅ 服务 DI | ✅ 热重载 |
317
+ | **OSGi + DS** | Java | ✅* bundle stop + `@Deactivate` | ✅ DS `bind/unbind/reconfigure` | ✅ |
318
+ | Effect-TS | TS | ✅ Scope/LIFO finalizers | ❌ 无响应式服务激活 | 部分 |
319
+ | VS Code Extension Host | TS | ❌ 手动 dispose,无法原地重置 | ❌ activation 一次性 | ❌ 须宿主重载 |
320
+ | Inversify / NestJS / Angular | TS | ❌ OnDestroy 是 shutdown 语义 | ❌ 容器图静态 | ❌ |
321
+ | Spring(core) | Java | ❌ Spring-DM 已移除 | ❌ `@RefreshScope` 非响应式激活 | ❌ |
322
+ | .NET(MEF/Autofac) | C# | 部分(程序集无法卸载) | 部分 recomposition | ❌ 须 AppDomain |
323
+ | Umzug(迁移) | JS | ✅* 仅时间维(up/down) | ❌ | N/A |
324
+ | webpack/Tapable、Rollup、esbuild | JS | N/A 构建期 | ❌ | N/A |
325
+
326
+ **判定**:OSGi 是 Cordis 之前唯一同构的双支柱系统(且免重启卸载);Cordis 的差异化不在
327
+ "有没有"而在**严谨度 + 人体工学**——每个 ctx 变更都带运行时追踪的逆函数、逆序重放(路径无关/
328
+ 合流性),OSGi 的 `deactivate()` 可靠但依赖作者纪律。Koishi 是直系祖先。Effect-TS 只到时间维;
329
+ VS Code 是论文要反对的反例;Nest/Angular/Inversify/Spring/MEF 是空间维或 teardown-only;
330
+ Umzug 是相邻的时间维-only;bundler 是构建期。**Cordis 的主张在"类"上不唯一,但在托管语言里
331
+ 是形式化根基最干净、免重启实现最彻底的一个。**
332
+
333
+ ---
334
+
335
+ ## 来源
336
+
337
+ - `github.com/cordiverse/cordis`(TypeScript/MIT,`packages/{core,create,group,hmr,include,
338
+ loader,logger-console,timer,utils}`)
339
+ - `github.com/cordiverse/paper` —— *A Programming Paradigm for Spatiotemporal Composability*
340
+ (Draft 2026-08-13)
341
+ - 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`
349
+ - 竞品:OSGi Core Spec 9.0、`github.com/koishijs/koishi`、Effect v3 Scope、VS Code extension
350
+ anatomy
@@ -0,0 +1,186 @@
1
+ # DSH Web Profile 包分类测绘
2
+
3
+ > 记录:2026-08-31 · 一手核验:`~/.dsh/profiles/node_modules/@deepseek-ai/`(220 包,dsh-alpha
4
+ > 4.x 全量)+ `~/workspaces/dsh-alpha/packages/` 源码(client 各包 src import 逐文件 grep)。
5
+ > 范围:Web Profile(含 headless-only 包一并列出,但标注为"非 Web")。
6
+
7
+ ---
8
+
9
+ ## 1. 一句话结论
10
+
11
+ **DSH Web 的依赖不是一条干净的"核心 → 中间层 → UI"三层管道,而是"双轨":**
12
+
13
+ - **运行时数据**走干净的管道:`核心(service) → 中间层(api-controller RPC) → UI`。
14
+ - **类型**却是**双轨旁路**:UI 层**跳过中间层,直接 import 底层核心的类型**
15
+ (`dsh-session/types`、`dsh-llm`、`dsh-agent`、`dsh-scope`)。
16
+
17
+ **量化证据**:40 个 `dsh-client-ui-*` 包里,**17 个直接 import 底层核心**(session/llm/agent/scope),
18
+ 共约 70 个文件;其中 `ui-conversation`、`ui-chat` 各 14 个文件。这是"UI 直接 import 底层"的铁证。
19
+
20
+ ---
21
+
22
+ ## 2. 数据流验证(你问的核心问题)
23
+
24
+ ### 2.1 UI 直接 import 底层核心(跳过中间层)
25
+
26
+ ```
27
+ ui-conversation: 14 文件
28
+ ui-chat: 14 文件
29
+ ui-workspace: 5 文件
30
+ ui-workflow-run: 3 文件
31
+ ui-trajectory: 3 文件
32
+ ui-subagent: 3 文件
33
+ ui-input-trigger: 3 文件
34
+ ui-message-feedback: 2 文件
35
+ ui-goal: 2 文件
36
+ ui-commands: 2 文件
37
+ + 7 个包各 1 文件(session / skill / plan / model-selection / deliverables / approval / user-questions)
38
+ ```
39
+
40
+ 具体例子(`ui-session/src/client/index.ts`):
41
+
42
+ ```ts
43
+ import type { SessionId } from '@deepseek-ai/dsh-session/types' // 直接 import 底层
44
+ import type {} from '@deepseek-ai/dsh-api-session-controller/client' // 同时 import 中间层
45
+ ```
46
+
47
+ ### 2.2 判定
48
+
49
+ **是,UI 层既走中间层(拿运行时数据),又跳过中间层(拿类型)。** 两条轨:
50
+
51
+ | 轨 | 路径 | 内容 |
52
+ |---|---|---|
53
+ | 数据轨 | 核心 service → api-controller → RPC → UI | 运行时数据(会话列表、transcript、模型) |
54
+ | 类型轨 | 核心 types → UI **直接 import** | `SessionId`/`SessionEvent`/`ContentBlock` 等形状 |
55
+
56
+ **这正是"UI 类型耦合在底层、不在中间层"的机制层证据**——你之前问"alias dsh-session 能不能
57
+ 只服务前端 100 行",答案是**不能**,因为前端 17 个包、约 70 个文件直接 import 了 dsh-session/
58
+ dsh-llm 的类型,alias 必须覆盖这些。
59
+
60
+ ---
61
+
62
+ ## 3. 四层分类清单(220 包)
63
+
64
+ ### ① Cordis 框架底层(15)
65
+
66
+ | 组 | 包 |
67
+ |---|---|
68
+ | 框架核心 | `cordis` |
69
+ | 框架插件 | `cordis-plugin-loader` `cordis-plugin-include` `cordis-plugin-group` `cordis-plugin-timer` `cordis-plugin-hmr` |
70
+ | 框架配套 | `cosmokit` `schemastery` `node-addon-landlock-run` |
71
+ | 共享基础 | `dsh-invariants` `dsh-brand` `dsh-timeout` `dsh-atomic-write` `dsh-util-crypto` `dsh-util-workspace-path` |
72
+
73
+ ### ② DSH 核心底层(约 140,含 headless)
74
+
75
+ | 组 | 包 |
76
+ |---|---|
77
+ | 入口/自举 | `dsh` `dsh-base` `dsh-app-boot` `dsh-home-paths` `dsh-launch-environment` |
78
+ | host 基建 | `dsh-host-webserver` `dsh-host-frontend-static` `dsh-host-directory-picker`(+auto/browse/native) `dsh-host-plugin-inventory` |
79
+ | **Session(17)** | `dsh-session` `dsh-session-persistence` `dsh-session-persistence-jsonl` `dsh-session-projection` `dsh-session-projection-cache` `dsh-session-query` `dsh-session-query-sqlite` `dsh-session-title` `dsh-session-title-first-prompt-llm` `dsh-session-title-llm` `dsh-session-log-deepseek` `dsh-session-log-export` `dsh-session-stats` `dsh-session-telemetry` `dsh-session-telemetry-otel` `dsh-session-checkpoint-policy` `dsh-session-reference` |
80
+ | **LLM(5)** | `dsh-llm` `dsh-llm-deepseek` `dsh-llm-pi-ai` `dsh-llm-retry` `dsh-deepseek-llm-api-extensions` |
81
+ | **Agent(7)** | `dsh-agent` `dsh-agent-loop` `dsh-agent-presets` `dsh-agent-default-model` `dsh-agent-instructions` `dsh-agent-tool-presentation` `dsh-agent-spine-demo` |
82
+ | scope/workspace | `dsh-scope` `dsh-workspace` |
83
+ | **Tools(20)** | `dsh-tools` `dsh-tool-{ask-user,bash,bash-persistent,call-timeout-policy,cordis,fs,fs-search,goal,jobs,pwsh,pwsh-persistent,ralph,skill,str-replace-editor,subagent,subagent-control,subagent-report,todo,web,workflow}` |
84
+ | 命令/技能 | `dsh-commands` `dsh-command-{compact,feedback,goal}` `dsh-skill` `dsh-skill-{badge,filesystem}` |
85
+ | 子代理/子进程 | `dsh-subagent` `dsh-subagent-{fork-in-process,in-process-driver,spawn-in-process}` `dsh-subprocess` `dsh-subprocess-local` |
86
+ | shell/sandbox/fs | `dsh-shell` `dsh-shell-env` `dsh-bash-{local,sandbox}` `dsh-pwsh-{local,sandbox}` `dsh-sandbox` `dsh-sandbox-{local,policy,windows-acl}` `dsh-fs` `dsh-fs-{local,observation-policy,sandbox}` `dsh-win32-process` |
87
+ | code-runtime | `dsh-code-runtime` `dsh-code-runtime-worker-thread` |
88
+ | 存储/spill/压缩 | `dsh-storage` `dsh-storage-{domain,json}` `dsh-spill` `dsh-spill-{local,policy}` `dsh-compaction` `dsh-compaction-{basic,tool-result-pruner}` |
89
+ | jobs/attachment/credential | `dsh-jobs` `dsh-jobs-local` `dsh-attachment` `dsh-attachment-local` `dsh-credentials` `dsh-credentials-local` |
90
+ | 权限/审批 | `dsh-permission-presets` `dsh-authorization` `dsh-user-approval` `dsh-user-questions` |
91
+ | 消息/引用 | `dsh-message-feedback` `dsh-file-reference` `dsh-file-reference-local` |
92
+ | goal/plan/persona/system | `dsh-goal` `dsh-goal-round-driver` `dsh-plan-mode` `dsh-persona` `dsh-system-prompt` `dsh-schedule` `dsh-time-context` `dsh-tmux-context` |
93
+ | hooks | `dsh-hook-protocol` `dsh-hooks-claude-code` `dsh-hooks-codex` |
94
+ | MCP/杂项 | `dsh-mcp-client` `dsh-token-meter` `dsh-output-retention` `dsh-native-command` `dsh-repeat-tool-reminder` `dsh-settings` `dsh-settings-file` `dsh-workflow` `dsh-workflow-worker-thread` `dsh-plugin-package-inventory-deepseek` |
95
+ | *headless-only* | `dsh-headless` `dsh-cmdline` `dsh-terminal` `dsh-terminal-bash` |
96
+
97
+ ### ③ DSH 中间层(API/RPC 转接,约 16)
98
+
99
+ | 组 | 包 |
100
+ |---|---|
101
+ | API BFF | `dsh-api-gateway` `dsh-api-remotes` `dsh-api-session-controller` `dsh-api-settings-controller` `dsh-api-workspace-controller` |
102
+ | RPC 机制 | `dsh-typert-protocol` `dsh-typert-registry` `dsh-typert-loader` |
103
+ | RPC 传输 | `dsh-client-connection` |
104
+ | 双半 runner | `dsh-cordis-host-runner` `dsh-cordis-client-runner` |
105
+ | ACP | `dsh-acp` `dsh-acp-app` |
106
+ | SDK | `dsh-sdk-app` `dsh-sdk-jsonrpc-server` `dsh-sdk-minimal` `dsh-sdk-protocol` |
107
+
108
+ ### ④ DSH UI 层(约 50)
109
+
110
+ | 组 | 包 |
111
+ |---|---|
112
+ | **UI 组件(40)** | `dsh-client-ui-{agent-preset,approval,attachment,brand-official,chat,commands,conversation,cordis,deliverables,directory-picker-browse,directory-picker-native,goal,input-trigger,jobs,layout,message-feedback,model-selection,permission-presets,plan,reference,renderer,session,settings,settings-general,settings-models,settings-plugin-inventory,settings-plugins,sidebar,skill,subagent,theme,tool,trajectory,user-questions,workflow-run,workspace}`(40 个) |
113
+ | client 基建 | `dsh-client-modules` `dsh-client-locale` `dsh-client-hmr` `dsh-client-store` |
114
+ | web 组装 | `dsh-web` `dsh-web-app` `dsh-web-frontend` |
115
+ | web 工具 | `dsh-web-fetch-http` `dsh-web-search-deepseek` `dsh-webhook` `dsh-webhook-github` |
116
+
117
+ ---
118
+
119
+ ## 4. Mermaid 分层图
120
+
121
+ ```mermaid
122
+ flowchart TD
123
+ subgraph L1["① Cordis 框架底层"]
124
+ cordis["cordis"]
125
+ cp["cordis-plugin-loader / include / group / timer / hmr"]
126
+ base["cosmokit · schemastery · node-addon-landlock-run"]
127
+ shared["dsh-invariants · dsh-brand · dsh-timeout · dsh-atomic-write · dsh-util-*"]
128
+ end
129
+
130
+ subgraph L2["② DSH 核心底层"]
131
+ boot["dsh · dsh-base · dsh-app-boot · dsh-home-paths"]
132
+ host["dsh-host-webserver · host-frontend-static · host-directory-picker · host-plugin-inventory"]
133
+ session["dsh-session · session-persistence · session-projection · session-query · session-title · session-log · session-stats · session-telemetry"]
134
+ llm["dsh-llm · llm-deepseek · llm-pi-ai · llm-retry · deepseek-llm-api-extensions"]
135
+ agent["dsh-agent · agent-loop · agent-presets · agent-default-model · agent-instructions"]
136
+ scope["dsh-scope · dsh-workspace"]
137
+ tools["dsh-tools · dsh-tool-*(20)· dsh-command-* · dsh-skill-* · dsh-subagent-*"]
138
+ runtime["dsh-subprocess · shell · bash · pwsh · sandbox · fs · code-runtime · storage · spill · compaction · jobs · attachment · credentials"]
139
+ policy["dsh-permission-presets · authorization · user-approval · hook-protocol · mcp-client"]
140
+ end
141
+
142
+ subgraph L3["③ DSH 中间层(API/RPC 转接)"]
143
+ api["dsh-api-gateway · api-remotes · api-session-controller · api-settings-controller · api-workspace-controller"]
144
+ rpc["dsh-typert-protocol · typert-registry · typert-loader · client-connection"]
145
+ runner["dsh-cordis-host-runner · cordis-client-runner"]
146
+ ext["dsh-acp · acp-app · dsh-sdk-app · sdk-jsonrpc-server · sdk-protocol"]
147
+ end
148
+
149
+ subgraph L4["④ DSH UI 层"]
150
+ renderer["dsh-client-ui-renderer(SlotRegistry + React mount)"]
151
+ shell["dsh-client-ui-layout · sidebar · theme"]
152
+ chat["dsh-client-ui-chat · session · conversation · trajectory · message-feedback"]
153
+ model["dsh-client-ui-model-selection · agent-preset · permission-presets"]
154
+ ws["dsh-client-ui-workspace · goal · plan · jobs · workflow-run · deliverables"]
155
+ misc["dsh-client-ui-* 其余(approval · attachment · commands · reference · skill · subagent · tool · user-questions · input-trigger · settings-* · directory-picker-* · cordis · brand-official)"]
156
+ infra["dsh-client-modules · client-locale · client-hmr · client-store"]
157
+ web["dsh-web · dsh-web-app · dsh-web-frontend"]
158
+ end
159
+
160
+ L2 -->|"运行时数据(service → RPC)"| L3
161
+ L3 -->|"运行时数据(RPC → 浏览器)"| L4
162
+ L2 -.->|"类型直连:SessionId / SessionEvent / ContentBlock(跳过中间层,17 个 UI 包约 70 文件)"| L4
163
+ L1 --> L2
164
+ ```
165
+
166
+ ---
167
+
168
+ ## 5. 关键发现汇总
169
+
170
+ 1. **不是干净三层管道**:数据走 `核心→中间层→UI`,但**类型走 `核心→UI` 旁路**。
171
+ 2. **类型耦合在底层,不在中间层**:`dsh-client-ui-session` 直接 `import { SessionId } from
172
+ 'dsh-session/types'`,`ui-chat`/`ui-conversation` 各 14 文件直接 import dsh-session/dsh-llm。
173
+ 3. **对桥接层的意义**:要斩断 dsh-session,代价不在 api-controller(中间层),而在 UI 层那
174
+ ~70 个文件的类型 import——这才是真正的耦合点。
175
+ 4. **中间层(api-controller)是运行时数据的分发器**,不是类型隔离层;类型隔离在 UI 层直接
176
+ import 底层时就破了。
177
+
178
+ ---
179
+
180
+ ## 来源
181
+
182
+ - `~/.dsh/profiles/node_modules/@deepseek-ai/`:220 包清单(`ls`)+ 各包 `package.json` 的
183
+ `description`/`peerDependencies`/`dependencies`。
184
+ - `~/workspaces/dsh-alpha/packages/client/`:`ui-*/src` 逐文件 grep `import ... from
185
+ '@deepseek-ai/dsh-(session|llm|agent|scope)'` 与 `'@deepseek-ai/dsh-api-*-controller/client'`。
186
+ - 关联:`dsh-web-ui-slot-system-research.md`(UI 插槽机制)、`cordis-research.md`(Cordis 服务端)。