better-dsh 0.2.5-a → 0.2.5-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 (51) hide show
  1. package/THIRD_PARTY_NOTICES.md +49 -0
  2. package/docs/50_test-reports/2026-10-02-better-dsh-schemastery/344/276/235/350/265/226/345/243/260/346/230/216/344/277/256/345/244/215/345/256/236/346/265/213/346/212/245/345/221/212.md +62 -0
  3. package/docs/50_test-reports/2026-10-03-better-dsh-tool-surface-repl-dvc-browser-ast-lsp-4999/345/256/236/346/265/213/346/212/245/345/221/212.md +185 -0
  4. package/docs/50_test-reports/2026-10-03-physical-import/344/270/216llm-completion-ast/347/233/270/345/257/271/350/267/257/345/276/204/344/277/256/345/244/215/345/256/236/346/265/213/346/212/245/345/221/212.md +101 -0
  5. package/docs/50_test-reports/2026-10-03-schemastery-cosmokit/346/272/220/347/240/201/345/206/205/350/201/224/345/256/236/346/265/213/346/212/245/345/221/212.md +63 -0
  6. package/docs/50_test-reports/2026-10-03-zero-npm-runtime-deps/345/256/236/346/265/213/346/212/245/345/221/212.md +82 -0
  7. package/docs/specs/zero-npm-runtime-deps/spec.md +85 -0
  8. package/lib/BrowserWebSocketTransport-FbPg_VFT.js +39 -0
  9. package/lib/BrowserWebSocketTransport-Q5SUMk7_.js +4 -0
  10. package/lib/LaunchOptions-DlBKKKLC.js +4 -0
  11. package/lib/LaunchOptions-RNO1YIoJ.js +17 -0
  12. package/lib/NodeWebSocketTransport-BdrEPUFe.js +3629 -0
  13. package/lib/NodeWebSocketTransport-DyU2tm5L.js +4 -0
  14. package/lib/bidi-D0POLRy7.js +16106 -0
  15. package/lib/chunk-DQxbwPpZ.js +30 -0
  16. package/lib/compaction/index.d.ts +3 -3
  17. package/lib/compaction/index.js +4 -4
  18. package/lib/failover/index.d.ts +3 -3
  19. package/lib/failover/index.js +4 -4
  20. package/lib/fs-aware/sandbox-plugin.js +20 -20
  21. package/lib/index-DZQNwO7f.d.ts +208 -0
  22. package/lib/index-browser-rqlXyoed.js +21061 -0
  23. package/lib/index.d.ts +4 -4
  24. package/lib/index.js +397 -149
  25. package/lib/kernel-bridge.py +172 -0
  26. package/lib/kernel-env.js +1 -1
  27. package/lib/lib-C8_jXvwA.js +894 -0
  28. package/lib/{lsp-server-registry-946ysdqg.js → lsp-server-registry-C05QLdw0.js} +13 -13
  29. package/lib/{lsp-server-registry-XU6aHSqZ.js → lsp-server-registry-LUjrrLxk.js} +1 -1
  30. package/lib/main-7c69ae95.js +1432 -0
  31. package/lib/main-Dlv0Jm8U.js +3 -0
  32. package/lib/mobile/plugin.d.ts +4 -4
  33. package/lib/mobile/plugin.js +10 -10
  34. package/lib/puppeteer-core-BIIJGWPE.js +1136 -0
  35. package/lib/py-sdk.js +1 -1
  36. package/lib/remote/plugin.d.ts +3 -3
  37. package/lib/remote/plugin.js +9 -9
  38. package/lib/url-schemes/index.d.ts +11 -11
  39. package/lib/url-schemes/index.js +4274 -308
  40. package/lib/util-DgR7HQUl.js +2953 -0
  41. package/lib/web-password.d.ts +7 -7
  42. package/lib/web-password.js +2 -2
  43. package/lib/{web-trust-BO66y72w.js → web-trust-B27wcZEw.js} +2 -2
  44. package/lib/{web-trust-BihHCelF.d.ts → web-trust-PIXuF0bN.d.ts} +7 -7
  45. package/lib/web-trust.d.ts +1 -1
  46. package/lib/web-trust.js +2 -1
  47. package/lib/{wrap-Bzdimp4p.js → wrap-0VA0uFx8.js} +54 -51
  48. package/package.json +9 -21
  49. /package/lib/{kernel-env-hxaihi9C.js → kernel-env-BLPZE-I3.js} +0 -0
  50. /package/lib/{native-capture-x_Hos2e8.js → native-capture-BEByDXYM.js} +0 -0
  51. /package/lib/{py-sdk-Chvy92MB.js → py-sdk-D7klhpWb.js} +0 -0
@@ -0,0 +1,49 @@
1
+ # Third-party notices
2
+
3
+ `better-dsh` ships source copies of three MIT-licensed packages under
4
+ [`third_party/`](third_party/). They are **vendored, not dependencies**: the
5
+ plugin must not depend on how a host installation scope resolves them, so the
6
+ sources live here, are imported by relative path, and are inlined into the
7
+ published `lib/` bundle. Provenance, version pins, and the update procedure are
8
+ documented in [`third_party/README.md`](third_party/README.md).
9
+
10
+ | Package | Version | License | Upstream |
11
+ |---|---|---|---|
12
+ | `@deepseek-ai/schemastery` | 3.18.4 | MIT | <https://github.com/deepseek-ai/deepseek-harness> |
13
+ | `@deepseek-ai/cosmokit` | 1.8.5 | MIT | <https://github.com/deepseek-ai/deepseek-harness> |
14
+ | `@standard-schema/spec` | 1.1.0 | MIT | <https://github.com/standard-schema/standard-schema> |
15
+
16
+ `@standard-schema/spec` is a type-only import of `schemastery`'s declarations;
17
+ `cosmokit` is `schemastery`'s sole runtime dependency and is resolved from
18
+ `third_party/schemastery/node_modules/`.
19
+
20
+ ## MIT License
21
+
22
+ Applies to all three packages above, each with its own copyright holder:
23
+
24
+ ```
25
+ MIT License
26
+
27
+ Copyright (c) 2021-present Shigma (schemastery, cosmokit)
28
+ Copyright (c) 2024 Colin McDonnell (@standard-schema/spec)
29
+
30
+ Permission is hereby granted, free of charge, to any person obtaining a copy
31
+ of this software and associated documentation files (the "Software"), to deal
32
+ in the Software without restriction, including without limitation the rights
33
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34
+ copies of the Software, and to permit persons to whom the Software is
35
+ furnished to do so, subject to the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be included in all
38
+ copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
46
+ SOFTWARE.
47
+ ```
48
+
49
+ Each copy also retains its upstream `LICENSE` file in place.
@@ -0,0 +1,62 @@
1
+ # better-dsh schemastery 依赖声明修复实测报告(2026-10-02)
2
+
3
+ - **对象**:better-dsh **0.2.5 → 0.2.5-a** —— `@deepseek-ai/schemastery` 从 `dependencies` 改为 optional **peer**(commit `c4018aa`)
4
+ - **症状(用户报告)**:prod 实例每次启动都出现
5
+ `dashr-failover (better-dsh/failover): failed to import` +
6
+ `dashr-compaction-tuning (better-dsh/compaction-tuning): failed to import`
7
+ (本机 PC 3080 journal 自 **Sep 29** 起、共 **10 次 boot** 均复现,另附 `corti-memory`;dev3 prod 3080 同样两行)
8
+ - **载体**:本机 PC 3080 / dev3 prod 3080(dsh **0.2.0-rc.2**)+ 干净 profile 单变量复现 + dev3 全新重装验收
9
+
10
+ ## 一、根因
11
+
12
+ **链条**:better-dsh 把 `@deepseek-ai/schemastery` 写在自己的 **`dependencies`** → pnpm 在 `profiles/<p>/node_modules/@deepseek-ai/schemastery` **实体化出第二份** → Node 就近 walk-up 先命中这一份,**永远走不到 harness 的 installation-scope 拦截** → 该份被 profile 的 lockfile 冻在 **3.18.2**,而宿主已随 dsh 升到 **3.18.4**。
13
+
14
+ **为什么致命**:`.volatile()`(dsh 0.1.7+ settings 模型所需)**3.18.3 才引入**。better-dsh 的 `failover` / `compaction` 在**模块作用域**用 `.volatile()` 建 Config schema,导入即抛 → loader 记为 `failed to import`(fiber 根本没建)。
15
+
16
+ | 构建产物 | `volatile` 命中 | 实测 |
17
+ |---|---|---|
18
+ | `lib/failover/index.js` | 4 | **FAILS** |
19
+ | `lib/compaction/index.js` | 4 | **FAILS** |
20
+ | `lib/url-schemes/index.js` | 0 | OK |
21
+ | `lib/index.js`(main) | 0 | OK |
22
+
23
+ **harness 本来就已经供给它**(app-boot `collectInstallationScopePackages`):从已安装的 `@deepseek-ai/dsh` manifest 出发,BFS 走 **`dependencies ∪ peerDependencies` 传递闭包**,闭包内每个名字对**所有 profile** 生效。而 **dsh 自己 declare 了 `@deepseek-ai/schemastery: ~3.18.4`**,宿主树内即有(`…/dsh/node_modules/@deepseek-ai/schemastery`,**3.18.4**,带 `.volatile()`)。所以指针本应指向宿主,被我们自己搬到了 profile 层。
24
+
25
+ ## 二、修复
26
+
27
+ `c4018aa`:把 `@deepseek-ai/schemastery` 从 `dependencies` **删除**,只保留 optional peer 声明(与 `@deepseek-ai/dsh-*` 等其他 harness 包同款)。这样 pnpm 不再在 profile 里物化副本,解析回落到 installation scope(宿主的 3.18.4)。
28
+
29
+ (前一轮 `f11dda8` 的 floor bump `^3.18.1 → ^3.18.3` 保留为 0.2.5 存量用户的桥;peer 修正落地后不再有实际作用。**已按 §〇.5 撤回自包含/vendoring 方案** —— DSH 已 bundle 过一次,ride-on-DSH 的插件不应再抄一份。)
30
+
31
+ ## 三、验证
32
+
33
+ **1) 单变量复现/对照(决定性)**:一个 boot 完全干净的 profile,把 schemastery 从 3.18.4 换成 **3.18.2** → 重启后**恰好这两行**失败;换回即干净。单一变量、单一结果。
34
+
35
+ **2) 新 profile 装修正版**:profile 内 **`@deepseek-ai/` 整个目录都不存在**;boot **零 `did not activate`**。
36
+
37
+ **3) dev3 全新重装验收(第一人称,按 user 指令做的实验)**:
38
+ - 保留全部 user 数据(`sessions`/`storages`/`credentials`/`superd`/`task-board`),`cordis.patch.yml`(111 行用户层)原样恢复;
39
+ - 清空 `~/.dsh/profiles`(2.4G)与全局 DSH → DSH **从 registry** 重装 `0.2.0-rc.2`;
40
+ - profile 重建:DSH/SuperD **从 registry**,**better-dsh 用本次修正 tarball**;
41
+ - 结果对照:
42
+
43
+ | | 修复前 | 修复后 |
44
+ |---|---|---|
45
+ | `dashr-failover`/`dashr-compaction-tuning` `failed to import` | **2** | **0** |
46
+ | `dsh: warning: N entries did not activate` | 有 | **整行消失** |
47
+ | profile 层 `node_modules/@deepseek-ai/` | schemastery 3.18.2 + cosmokit | **不存在** |
48
+ | 正向验证 `curl :3080/` | — | **200 + `Enter the session password`**(web-password 行活着,独占 exact `/`) |
49
+
50
+ - 剩余 warning 属 **super-dsh**(`superd-hermes`/`superd-codex` 各 1 条 `session-controller: pending (waiting for service: fileUploads)`)——**pending 非 failed,且修复前的 boot 同样存在**,与本修复无关。
51
+
52
+ **4) 单元/类型**:`tsc --noEmit` **0 错**;`vitest run` **649 passed / 1 skipped**(57 文件)。
53
+
54
+ ## 四、附:一个独立的运维坑(非本 bug)
55
+
56
+ 本机曾出现 `dsh plugin add/remove` 报 `node_modules was installed with a different major version of pnpm`。根因 = **两个 pnpm 版本打架**:`/home/u1/.local/bin/pnpm` = 11.7.0(profile 的 node_modules 由它建,store **v11**)、`/usr/local/bin/pnpm` = **10.29.2**;某些调用上下文 PATH 不含 `~/.local/bin` → harness 调到 pnpm 10 去动 v11 建的树 → 硬错。
57
+
58
+ **已修**:`sudo npm i -g pnpm@11.7.0` → 两个路径均 **11.7.0**;模拟"`~/.local/bin` 掉出 PATH"的最坏情况仍得 11.7.0。dev3 全程用其本地 pnpm 10.33.2 干净完成安装,证明**该 bug 与 pnpm 无关**。
59
+
60
+ ## 五、判定
61
+
62
+ **通过**。根因单一且已用单变量复现 + 全新重装双重证明;修复面只有一个声明位的移动;dev3 第一人称验收 0 失败。发布 `0.2.5-a`。
@@ -0,0 +1,185 @@
1
+ # better-dsh 0.2.5-a 工具面实测报告:REPL / dvc://browser / dvc://ast_grep / dvc://ast_edit / dvc://lsp(4999 rig)
2
+
3
+ > **状态**:第一人称实测完成(agent 在真实 4999 会话内自测并留证)。
4
+ > **性质**:工具面**冒烟 + 回归**,**不是**发布验收——本轮未走 AGENTS.md §〇 的 a–d 发布闸,
5
+ > 不涉及也不授权 `npm publish`。
6
+ > **被测版本**:better-dsh `0.2.5-a`(package.json 与 profile 实装双侧一致);core `@deepseek-ai/dsh` `0.2.0-rc.2`。
7
+ > **仓库 commit**:`fb49834`(根仓与 `better-dsh/` 同 SHA)。
8
+ > **rig**:`.test/home/compat`,端口 4999,unit `dsh-4999-test123`;本轮**未重启、未改产物、未动 prod**。
9
+ > **时间**:2026-10-03 01:40–01:56 HKT(2026-10-02 17:40–17:56 UTC)。
10
+ > **相关前序**:[2026-09-27-lsp-ast-reminder-4999实测报告.md](2026-09-27-lsp-ast-reminder-4999实测报告.md)(lsp/ast 提醒的发布轮)。
11
+
12
+ ---
13
+
14
+ ## 0. 方法
15
+
16
+ - 全部证据取自**本会话内的真实工具调用**(第一人称),非单测、非静态检查、非进程活性。
17
+ - `dvc://` 设备经 `write dvc://<device>` 执行,`read dvc://<device>` 仅取用法;REPL 证据取自 `eval` 单元。
18
+ - 临时产物集中在 `.scratch/ast-probe/`;browser 设备测毕已 `close`(无孤儿 Chrome)。
19
+
20
+ ## 1. 结论摘要
21
+
22
+ | # | 面 | 结论 | 关键证据 |
23
+ |---|---|---|---|
24
+ | 1 | REPL(`eval`) | ✅ 全绿 | 跨 cell 状态、完成值、工具桥、错误信封、异常存活 7/7 |
25
+ | 2 | `dvc://browser` | ✅ 功能全绿;⚠️ 进不去带密码闸的 GUI | open/run/type+click/close + 字段白名单全通 |
26
+ | 3 | `dvc://ast_grep` | ✅ 可用;⚠️ 相对路径基准非 workspace | 15 命中、`metaVariables`、`offset/limit` |
27
+ | 4 | `dvc://ast_edit` | ✅ dryRun 与实写均正确 | `dryRun:true` 不落盘;`false` → `applied:true` 且盘面核实 |
28
+ | 5 | `dvc://lsp` | ✅ gate + diagnostics + hover 全绿 | TS2322 error + TS6133 hint,坐标精确 |
29
+ | 6 | AST 可用性提醒 | ✅ 实测出现 | `write` 结果尾行一行提醒 |
30
+
31
+ 净结论:**四个 dvc 设备与 REPL 主体功能无回归**;发现 2 个可用性摩擦(F1/F2)与 1 个 API 面不一致(F3),
32
+ 均非阻断,建议并入下一次批量发布(AGENTS.md §〇.5)。
33
+
34
+ ## 2. REPL(`eval` / dashr-repl 桥)— 全绿
35
+
36
+ | 探针 | 结果 |
37
+ |---|---|
38
+ | 跨 cell 状态 | cell 1 设 `REPL_MARKER = "cell-1-alive"`,cell 2/4/6 读回一致 |
39
+ | 导入持久 | cell 3 `import asyncio, json, collections`,cell 4 仍绑定 |
40
+ | 完成值协议 | 末行裸表达式 `x`(=42)作为 completion value 返回,与 `print` 并存 |
41
+ | 工具桥 | `await tool.bash({"command": "echo bridge-ok"})` → `bridge-ok`,exit 0 |
42
+ | 错误信封 | 失败子调用抛 `ToolCallError`,`.toolName == "read"` |
43
+ | 入参校验 | `tool.read({})` → `[E_BAD_SHAPE] Read request requires a non-empty "path" string.` |
44
+ | 异常存活 | 内核在 SyntaxError、AttributeError 两次异常后仍持有全部状态 |
45
+ | 内核 | CPython `3.11.15`(profile 内 `.venv-kernel`) |
46
+
47
+ 桥接口径与 wire description 一致(`await tool.<name>(args)` 单位置参数对象,`true/false/null` → `True/False/None`)。
48
+
49
+ ## 3. `dvc://browser`
50
+
51
+ | 动作 | 结果 |
52
+ |---|---|
53
+ | `{"action":"open","url":"https://example.com"}` | `{ok:true, url, title:"Example Domain"}` |
54
+ | `{"action":"run","code":"…"}`(DOM eval) | 取回 `title/links/readyState`,JSON 字符串回传 |
55
+ | `{"action":"run","type":…,"click":…}` | 复合动作生效:`data:` 页 title 由 `t0` → `hello-dvc` |
56
+ | `{"action":"close"}` | `{ok:true}` |
57
+ | 未知字段 | `{"action":"open",…,"bogusField":1}` → `unknown field(s) "bogusField" for action "open" — allowed: "action", "url", "executablePath"` |
58
+
59
+ **⚠️ 限制(F2)**:指向本 GUI(`http://127.0.0.1:4999`)时,`?token=` 启动 URL **不能**为新浏览器铸出会话——
60
+ 页面停在 `Enter the session password to continue. PASSWORD Unlock`,`window.__DSH_BOOT__` 不存在、
61
+ 无任何 `better-dsh` 资源请求。因此 `dvc://browser` 目前**无法**用于对本 GUI 做端到端第一人称验收,
62
+ 除非补一条 cookie 注入/免密通道。(本 rig 是密码闸实例;token 只负责首铸 cookie,而全新 Chrome 无 cookie。)
63
+
64
+ **方法学附注(假阳性预防)**:首轮 DOM 探针返回 `h1count: 0`,一度疑似设备缺陷;
65
+ 核对线上原文后确认 **example.com 今日已无 `<h1>`**(仅 `<p>` + `<script>`),**设备是对的,探针是我的错**。
66
+ 记录此条以防后续把同类现象误判为插件 bug。
67
+
68
+ ## 4. `dvc://ast_grep`
69
+
70
+ | 项 | 结果 |
71
+ |---|---|
72
+ | 模式 | `defineTool($$$ARGS)`,`includeMeta:true` → 命中 15 / 13 文件 / 扫 189 文件,`limitReached:true` |
73
+ | 结构信息 | 每条含 `byteStart/byteEnd/startLine/startColumn/endLine/endColumn` + AST 级 `metaVariables` |
74
+ | 分页 | `offset:10, limit:2` → 精确返回 2 条(分页生效) |
75
+ | 语法错误容忍 | `defineTool({$$$})` 在 `defaults.json` 上语法不合法 → 该文件进 `parseErrors[]`,**整次搜索不失败**,其余 15 命中照常返回 |
76
+
77
+ **⚠️ 相对路径基准(F1)**:`path:"better-dsh/src"` → `Path not found: No such file or directory (os error 2)`;
78
+ 改为绝对路径即通。决定性实验:**`path:"../../better-dsh/src"` 命中 15 条,与绝对路径结果一致**。
79
+ 即 ast 设备的相对路径基准 = **harness 进程 cwd(`upstream/deepseek-harness`)**,而非会话 workspace
80
+ (`/home/u1/workspaces/dashr`)——与 `read/glob/grep` 的 workspace 基准**不一致**。
81
+ 返回值里的 `path` 也用同一基准显示(`../../better-dsh/src/bridges/index.ts`)。
82
+ **规避**:始终传绝对路径。
83
+
84
+ ## 5. `dvc://ast_edit`
85
+
86
+ 输入(作用于 scratch 文件):
87
+
88
+ ```json
89
+ {"ops":[{"pat":"const $NAME = function ($ARG) { $$$B }","out":"function $NAME($ARG) { $$$B }"}],
90
+ "paths":["/home/u1/workspaces/dashr/.scratch/ast-probe/sample.ts"],"dryRun":true}
91
+ ```
92
+
93
+ | 项 | 结果 |
94
+ |---|---|
95
+ | `dryRun:true`(默认) | 返回 `before`/`after` 与字节区间 `byteStart:74, byteEnd:128, deletedLength:54`;**`applied:false`,盘面未变** |
96
+ | `dryRun:false` | 同区间,**`applied:true`**;盘面核实:`const legacy = function (x: number) {…}` → `function legacy(x: number) { return x * 2 }` |
97
+ | `fileChanges`/`totalReplacements`/`filesTouched` | 1 / 1 / 1,与 `limitReached:false` 自洽 |
98
+
99
+ `paths` 同样受 F1 影响(本轮用绝对路径,未复现相对路径问题)。
100
+
101
+ ## 6. `dvc://lsp`
102
+
103
+ | 步骤 | 结果 |
104
+ |---|---|
105
+ | 初始 `status` | `{ok:true, gate:"unasked"}` —— per-session 闸,未询问态 |
106
+ | `{"action":"on"}` | `{ok:true, gate:"on", message:"lsp on for this session: servers warm-start per language on first file contact; no repo artifacts are written."}` |
107
+ | `diagnostics`(植入 2 处错) | `server:"typescript-language-server"`,自动推断 `root`;返回 **TS2322 error**(`Type 'number' is not assignable to type 'string'`,4:3)+ **TS6133 hint**(`'unusedVar' is declared but its value is never read.`,1:7);`summary:"1 error(s), 1 hint(s)"` |
108
+ | `hover`(3:17) | ```typescript\nfunction bad(x: number): string\n``` |
109
+
110
+ 坐标与 `severityName`(error/hint)均正确;服务器来源为 `typescript-language-server`。
111
+
112
+ ## 7. AST 可用性提醒 — 实测出现
113
+
114
+ 对新建 `.ts` 文件执行 `write`,结果尾部实测出现:
115
+
116
+ ```
117
+ ast_edit / ast_grep available — write dvc://ast_edit (structured rewrite) or dvc://ast_grep (pattern search)
118
+ ```
119
+
120
+ 与前序报告的制度一致(提醒挂 `write/edit/grep` 结果尾部、per-session cap)。
121
+ **注**:本轮未观测到 **LSP nag**(在接触代码文件前已把 gate 置 `on`,`unasked` 条件不再成立),故 nag 路径未覆盖;
122
+ 提醒的 **cap 计数**本轮亦未触及边界(未做 >5 次触发)。
123
+
124
+ ## 8. 发现清单
125
+
126
+ | ID | 严重度 | 面 | 现象 | 建议 |
127
+ |---|---|---|---|---|
128
+ | **F1** | 中(摩擦) | `dvc://ast_grep`/`ast_edit` | 相对 `path` 基准 = harness 进程 cwd(`upstream/deepseek-harness`),非会话 workspace;`"better-dsh/src"` 直接 `Path not found` | 二选一:①基准改为 workspace / `exec.cwd`;②保持现状但在 device help 明写"仅收绝对路径" |
129
+ | **F2** | 中(能力缺口) | `dvc://browser` | 无 cookie 注入通道,进不去密码闸 GUI;`?token=` 不足以对新浏览器铸会话 | 若要支撑 GUI 端到端第一人称验收,需补 cookie/免密通道 |
130
+ | **F3** | 中(API 一致性) | `llm_completion` | **返回类型不稳定**:成功回 **裸 `str`**(`'alpha beta gamma'`),失败回 **`dict`**(`{"error":"llm_completion() output reached its maxTokens ceiling"}`);调用方 `r["text"]` / `r.keys()` 会炸 | 统一为判别式对象(`{ok:true,text}` / `{ok:false,error}`) |
131
+ | **F4** | 低(调优) | REPL 子调用池 | 变更类子调用实际**串行**:4×`sleep 1` = 4.78s;6×`glob` = 14.71s。源码有 `maxParallelSubCalls`(默认 10)与 `parallel\|exclusive` 分类,但观测未见并行收益 | 复核 classify 是否把常用工具过度判为 `exclusive` |
132
+ | **F5** | 低(体验) | `llm_completion` | `maxTokens` 偏小(16)时因推理吃掉预算而**整次失败**而非截断 | 考虑截断或给出可操作的错误提示 |
133
+ | — | 观察(正面) | harness | 连续 5 次同参调用触发**重复调用护栏**并阻止了潜在死循环 | 保留 |
134
+
135
+ ## 9. 未覆盖 / 遗留
136
+
137
+ - `dvc://lsp` 的 `references` / `definition` / `format` / `rename` / `code_actions` / `reload` 未在本轮复测
138
+ (2026-09-27 报告已全绿;本轮只补 `status`/`on`/`diagnostics`/`hover`)。
139
+ - LSP **nag 路径**与 AST 提醒 **cap 边界**未覆盖(见 §7)。
140
+ - `dvc://ast_edit` 相对路径未复测(F1 已在 `ast_grep` 上定位)。
141
+ - `agent` / `agent_message` / `agent_workflow` / `remote` / 各 URL scheme(`ctx://` `skill://` `dsh://`)
142
+ 本轮未测(不属本次指定的工具面;`ctx://` 另有 2026-10-02 专项报告)。
143
+ - 生产 3080 **未部署验证**(本轮纯 4999,未触碰 prod)。
144
+
145
+ ## 10. 复现命令
146
+
147
+ ```bash
148
+ # REPL:在任意 4999 会话内
149
+ # eval: REPL_MARKER = "cell-1-alive" → 下一 cell: print(REPL_MARKER)
150
+
151
+ # ast_grep(务必绝对路径,或按 F1 用 ../../ 相对 harness 根)
152
+ # write dvc://ast_grep {"patterns":["defineTool($$$ARGS)"],
153
+ # "path":"/home/u1/workspaces/dashr/better-dsh/src","limit":5,"includeMeta":true}
154
+
155
+ # ast_edit dryRun → 实写
156
+ # write dvc://ast_edit {"ops":[{"pat":"const $NAME = function ($ARG) { $$$B }",
157
+ # "out":"function $NAME($ARG) { $$$B }"}],
158
+ # "paths":["<abs>/sample.ts"],"dryRun":true} # 再 dryRun:false 并 cat 盘面
159
+
160
+ # lsp
161
+ # write dvc://lsp {"action":"status"} # → gate:"unasked"
162
+ # write dvc://lsp {"action":"on"} # → gate:"on"
163
+ # write dvc://lsp {"action":"diagnostics","file":"<abs>/diag.ts"}
164
+ # write dvc://lsp {"action":"hover","file":"<abs>/diag.ts","line":3,"character":17}
165
+
166
+ # browser
167
+ # write dvc://browser {"action":"open","url":"https://example.com"}
168
+ # write dvc://browser {"action":"run","code":"document.title"}
169
+ # write dvc://browser {"action":"close"}
170
+ ```
171
+
172
+ ## 11. 附:本轮环境事实
173
+
174
+ | 项 | 值 |
175
+ |---|---|
176
+ | better-dsh | `0.2.5-a`(package.json ≡ profile 实装) |
177
+ | core | `@deepseek-ai/dsh` `0.2.0-rc.2` |
178
+ | commit | `fb49834` |
179
+ | rig home | `.test/home/compat` |
180
+ | 内核 | CPython `3.11.15`(`.venv-kernel`) |
181
+ | LSP 服务器 | `typescript-language-server` |
182
+ | browser 引擎 | Chromium(`puppeteer-core`,UA 含 `HeadlessChrome`) |
183
+
184
+ > 注:`readlink /proc/<pid>/cwd` 在本沙箱内取不到 4999 进程的 cwd(进程在另一命名空间),
185
+ > F1 的基准结论由**路径算术实验**(`better-dsh/src` 失败 vs `../../better-dsh/src` 全量命中)独立钉死,不依赖该读法。
@@ -0,0 +1,101 @@
1
+ # better-dsh:vendored 包改物理 import + `llm_completion` 返回形态 + ast 相对路径(2026-10-03)
2
+
3
+ - **对象**:better-dsh `0.2.5-a`(本地过程版本,未发布)
4
+ - **载体**:dev 树 + 4999 rig(`.test/home/compat`,unit `dsh-4999-test123`,core `0.2.0-rc.2`)
5
+ - **前序**:[2026-10-03-schemastery-cosmokit源码内联实测报告](2026-10-03-schemastery-cosmokit源码内联实测报告.md)、[2026-10-03-better-dsh-tool-surface-repl-dvc-browser-ast-lsp-4999实测报告](2026-10-03-better-dsh-tool-surface-repl-dvc-browser-ast-lsp-4999实测报告.md)(后者的 F1/F3 在本轮修复)
6
+
7
+ ## 一、vendored 包改为"物理 import"(去掉我上一轮加的 shim)
8
+
9
+ user 裁决:不要额外逻辑,**物理 import** 即可 —— 文件名就叫 `@deepseek-ai/<pkg>`,目标只是避开构建期/运行期的解析 logistics。
10
+
11
+ **终态**(41 个文件 / **348 KB**):
12
+
13
+ ```
14
+ third_party/
15
+ schemastery/ @deepseek-ai/schemastery 3.18.4 (MIT)
16
+ lib/index.mjs lib/index.cjs lib/types/…
17
+ lib/index.d.mts ← 唯一自加文件(见下)
18
+ src/index.ts LICENSE README.md package.json
19
+ node_modules/@deepseek-ai/cosmokit/ @deepseek-ai/cosmokit 1.8.5 (MIT)
20
+ node_modules/@standard-schema/spec/ 1.1.0 (MIT, 仅类型)
21
+ ```
22
+
23
+ `package.json` 里一个子路径映射(**删除了 `src/schemastery.ts`**):
24
+
25
+ ```json
26
+ "imports": {
27
+ "#schemastery": {
28
+ "types": "./third_party/schemastery/lib/index.d.mts",
29
+ "default": "./third_party/schemastery/lib/index.mjs"
30
+ }
31
+ }
32
+ ```
33
+
34
+ 9 个源文件统一 `import z from '#schemastery'`。
35
+
36
+ **两个必须知道的机制点**(都是实测撞出来的,不是猜的):
37
+
38
+ 1. **TS 拒绝把 `imports` target 指进 `node_modules` 段**。最初的镜像布局是
39
+ `third_party/node_modules/@deepseek-ai/schemastery/…`,`tsc` 报
40
+ `package.json scope … has invalid type for target of specifier '#schemastery'`;
41
+ 读 TS 源码定位到该诊断位于 `loadModuleFromTargetImportOrExport` 的
42
+ `!startsWith(target, "./")` 分支后,把 target 换成不含 `node_modules` 的路径即通。
43
+ 于是 schemastery **平铺**在 `third_party/schemastery/`,而它的依赖仍按真实安装形态
44
+ **嵌在它自己的 `node_modules/` 里**——这样 schemastery 内部的
45
+ `import '@deepseek-ai/cosmokit'` 就地解析,**vendored 代码一行都不用改**(`diff -rq`
46
+ 与上游逐字节一致,除我们新增的 `lib/index.d.mts`)。
47
+ 2. **`lib/index.d.mts` 这一个两行文件是必需的**:本包 `"type": "module"`,TS 会把
48
+ `lib/types/index.d.ts` 当 CJS 声明,直接 import `lib/index.mjs` 时取不到类型;
49
+ `.d.mts` 是 TS 的标准 ESM 声明位。
50
+
51
+ **副产品(比预期更好)**:类型面也不再引用外部 —— `lib/*.d.ts` 现在从**本地 chunk**
52
+ (`import { t as Schema } from "./index-DZQNwO7f.js"`)取 `Schema`,`lib/` 里既无
53
+ `#schemastery` 也无裸 `@deepseek-ai/schemastery`。即发布物**运行期 + 类型面双双自包含**。
54
+
55
+ ## 二、F3:`llm_completion` 返回形态统一(这是**我们自己**的工具)
56
+
57
+ 它是本插件自研的 one-shot / 无工具 / 无上下文的工具(`src/llm-completion.ts`),桥接上游
58
+ `ctx.llm` 服务;不是上游工具。原实现**成功回裸 `str`、失败回 `{ error }`**,程序化调用方
59
+ `r["text"]` / `r.keys()` 会炸。
60
+
61
+ 修法(三层一起改,缺一不可):
62
+
63
+ - 返回值:`{ ok: true, text }` / `{ ok: false, error }`(9 个错误点 + 1 个成功点);
64
+ - 工具 `output.schema`:`oneOf: [textVariant, errorVariant]`(原为 `oneOf: [string, errorVariant]`
65
+ —— **不改这里会直接报** `value must match exactly one oneOf branch (matched 0)`);
66
+ - `render`:成功时仍渲染纯文本(模型面可读),失败渲染 JSON;
67
+ - 描述补一句"直接调用成功时只渲染文本",消除"契约说对象、surface 是文本"的歧义(本轮第一人称
68
+ agent 如实报了这一点)。
69
+ - 测试同步:`test/surface-devices/llm-completion.spec.ts` 4/4 绿。
70
+
71
+ ## 三、F1:ast 设备的相对路径基准改为**会话 workspace**
72
+
73
+ 根因:eval 工具用的是 `exec.agent.session.header.cwd`(`src/index.ts:896`),而 dvc 写入这条路
74
+ 只把 `session` 传进设备(`dispatchDvcWrite(path, content, meta.session)`),没传 cwd,
75
+ `ast-device` 的 `ctxCwd()` 只能回落到**守护进程 cwd**(= `upstream/deepseek-harness`)。
76
+
77
+ 修法:把同一个 cwd 作为 transport 槽位补齐(不是 payload,避免触发设备的严格字段校验):
78
+
79
+ - `tools/write.ts`:`writeScheme(..., { session, ...exec.agent ? { cwd: exec.agent.session.header.cwd } : {} })`;
80
+ - `handlers/dvc.ts`:`dispatchDvcWrite(path, content, session?, cwd?)` → `device.execute(args, { session, ...(cwd ? { cwd } : {}) })`。
81
+
82
+ ## 四、验证
83
+
84
+ | 项 | 结果 |
85
+ |---|---|
86
+ | `tsc --noEmit`(host + client) | **0 错** |
87
+ | `npm run build` | 通过;产物 JS 无任何裸 `@deepseek-ai/schemastery\|cosmokit`;`lib/` 无 `#schemastery` |
88
+ | 全量单测 | **57 文件 / 649 passed, 1 skipped** |
89
+ | rig 安装 | 两个 profile 均 **`Packages: +1`** |
90
+ | rig 启动 | 零 `failed to import` / `did not activate` / `EPIPE`(上一轮修的 EPIPE:新 boot 段实测干净) |
91
+ | **F1 第一人称** | `write dvc://ast_grep {"patterns":["loadPiNatives"],"path":"better-dsh/src"}` → 命中 2 文件 / 3 处,且返回 `path` 为 **workspace 相对**(`better-dsh/src/devices/ast/ast-device.ts`) |
92
+ | **F3 第一人称** | `llm_completion({prompt:'只回答一个词:OK'})` → 程序化值为 `{ok:true,text:"OK"}`;agent 观测到的 surface 为纯文本 `OK`(render 层行为,描述已写明) |
93
+
94
+ ## 五、未做 / 限制
95
+
96
+ - **发布**:按 AGENTS.md §〇,`npm publish` 需 user 单次明确放行,本轮未发。
97
+ - `third_party/` **不随 tarball 发布**(`files` 不含它):`lib/` 已经内联了运行期与类型面,
98
+ 发布物自包含;vendored 源码只服务本仓构建。`package.json#imports` 因此在发布物里是
99
+ "指向未发布文件"的悬空映射 —— 发布物内没有任何代码 import 它(已实测 `lib/` 零引用)。
100
+ - `llm_completion` 的 F5(`maxTokens` 过小时整次失败而非截断)**未动**,见汇报;改动它会变更语义。
101
+ - F2(browser 无 cookie 注入通道)、F4(REPL 子调用未并行)未动。
@@ -0,0 +1,63 @@
1
+ # better-dsh:schemastery / cosmokit 源码内联实测报告(2026-10-03)
2
+
3
+ - **对象**:better-dsh `0.2.5-a`(本地过程版本,未发布)——`@deepseek-ai/schemastery` 从 **optional peer** 改为 **源码内联**(`third_party/`),同批内联其运行时依赖 `@deepseek-ai/cosmokit` 与(类型only的)`@standard-schema/spec`。
4
+ - **裁决**:user,2026-10-03 —— "这两个包直接抄进我们的源码里…这样我们就不必去看真实安装环境下它的上游到底在哪里、能不能够得着、解析路径对不对"。
5
+ - **载体**:本机 dev 树 + 4999 rig(`.test/home/compat`,unit `dsh-4999-test123`,core dsh `0.2.0-rc.2`)。
6
+
7
+ ## 一、为什么要内联(与前序报告的取舍关系)
8
+
9
+ [2026-10-02-schemastery依赖声明修复实测报告](2026-10-02-better-dsh-schemastery依赖声明修复实测报告.md) 记录过一次事故与一次撤回:
10
+
11
+ - 事故:schemastery 曾是我们的 **`dependencies`** → pnpm 在 profile 里**实体化出第二份**并被 profile 锁文件冻在 **3.18.2** → `.volatile()`(3.18.3+)缺失 → `failover`/`compaction` 在**模块作用域**建 schema 时抛错,loader 记为 `failed to import`。
12
+ - 撤回:当时结论是"DSH 已自带一份,ride-on-DSH 的插件不该再抄第二份",改为 optional peer,让解析回落到 harness installation scope。
13
+
14
+ **本次内联与那次撤回并不矛盾,因为失败模式不同**:
15
+
16
+ | | 当年(dependencies) | 本次(third_party 源码内联) |
17
+ |---|---|---|
18
+ | 版本从哪来 | profile 锁文件 / 上游解析 | **本仓 `third_party/` 钉死** |
19
+ | 会不会出现两份不同版本 | 会(宿主 3.18.4 vs profile 3.18.2) | 不会:我们只用自己这一份,且**不再声明该依赖** |
20
+ | 解析路径依赖 | 强(walk-up / installation scope) | 无(相对路径 import,构建期内联) |
21
+
22
+ 即:把"解析到哪一份"这个变量彻底消掉,而不是把它交给环境的解析顺序。
23
+
24
+ ## 二、改动
25
+
26
+ - 新增 `third_party/`(344K):
27
+ - `third_party/schemastery/` = `@deepseek-ai/schemastery@3.18.4`(MIT,Copyright (c) 2021-present Shigma)原样副本;
28
+ - `third_party/schemastery/node_modules/@deepseek-ai/cosmokit/` = `@deepseek-ai/cosmokit@1.8.5`(同版权人)——放这一层是为了让 schemastery 自己的 `import "@deepseek-ai/cosmokit"` **就地解析**,不必改动 vendored 代码;
29
+ - `third_party/schemastery/node_modules/@standard-schema/spec/` = `1.1.0`(MIT,Copyright (c) 2024 Colin McDonnell),仅类型。
30
+ - **唯一自加文件**:`third_party/schemastery/lib/index.d.mts`(转发到包自带的 `lib/types/index.d.ts`,因为直接 import `.mjs` 不会走它的 `exports.types` 映射)。
31
+ - `package.json` 增 `imports` 子路径映射 `#schemastery`(→ `./third_party/schemastery/lib/index.mjs`);9 个源文件(`index` / `runtime` / `web-password` / `web-trust` / `mobile` / `compaction` / `remote` / `failover` / `url-schemes`)统一 `import z from '#schemastery'`。**(后续已改为物理 import 形态并删除 `src/schemastery.ts`:见 [2026-10-03-physical-import与llm-completion-ast相对路径修复实测报告](2026-10-03-physical-import与llm-completion-ast相对路径修复实测报告.md))**
32
+ - `package.json`:删除 `@deepseek-ai/schemastery` peer 声明与其 `peerDependenciesMeta` 条目(peer 59 条 → 58 条生效面);`files` 增列 `THIRD_PARTY_NOTICES.md`。
33
+ - 署名:`THIRD_PARTY_NOTICES.md`(包根,随 tarball 发布:清单 + MIT 全文 + 各权利人)、`third_party/README.md`(provenance、布局、升级步骤、为何内联)。
34
+ - `.gitignore`:放行 `third_party/schemastery/node_modules/`(vendored 拷贝要入库)。
35
+
36
+ ## 三、验证
37
+
38
+ 1. **类型**:`tsc --noEmit`(host)与 `tsc -p tsconfig.client.json` 均 **0 错**。
39
+ 2. **构建**:`npm run build` 通过。产物 **JS 已内联**(`lib/schemastery-*.js` 分块,含 `createVolatile`/`isVolatile`);运行时**无任何**裸 `@deepseek-ai/schemastery|cosmokit` 引用(仅 `.d.ts` 里保留裸类型引用——与改动前行为一致,非回归;类型面宿主本就供给该包)。
40
+ 3. **单测**:`vitest run` **57 文件 / 649 passed, 1 skipped**。
41
+ 4. **4999 rig(第一人称)**:
42
+ - 安装 `Packages: +1`(仍只加自己);`deps`/`optional` 均为空,schemastery 已不在 peer 清单。
43
+ - **把 profile 树里的 `node_modules/@deepseek-ai/` 整个删掉**后启动:**零 `failed to import`、零 `did not activate`**;`--dump-config` 里 `dashr-failover` + `dashr-compaction-tuning` 两行在场——**正是当年挂掉的那两行**。
44
+ - rig 树里后来重新出现的 `@deepseek-ai/*` 来自 **profile 里的另一个插件 `corti-memory`**(其 `dependencies` 含 `@deepseek-ai/schemastery ^3.18.1`,lockfile 实证),与本插件无关:我们不再贡献该包。
45
+
46
+ ## 四、同批修掉的一个 crash 级 bug(本轮发现,非本次改动的目标)
47
+
48
+ rig 停机日志出现:
49
+
50
+ ```
51
+ dsh: fatal uncaught exception: Error: write EPIPE
52
+ at KernelTransport.send (…/better-dsh/lib/index.js)
53
+ at IpyKernelBridge.dispose
54
+ at DashrRuntime.teardownKernel
55
+ ```
56
+
57
+ 根因:round 2 新写的 `KernelTransport` 在 teardown 往**已死的 bridge stdin** 写 `shutdown`,`EPIPE` 以**异步 error 事件**到达,而没有 `error` 监听 → Node 升级为 uncaught exception(守护进程级)。修法:给 `child.stdin`/`stdout` 挂 error 监听(吞掉并把 transport 标死、结算 waiter),并把写操作收敛到 `writeLine()`:先查 `writable` 再写,失败抛普通 Error 而不是让管道错误逃逸。**实测**:重装后 boot + stop 全程日志零 `EPIPE`/`fatal uncaught`。
58
+
59
+ ## 五、未做 / 限制
60
+
61
+ - **未发布**:按 AGENTS.md §〇,`npm publish` 需 user 单次明确放行。
62
+ - ~~`.d.ts` 里仍以裸名引用 `@deepseek-ai/schemastery`~~ **已于同日解决**:改为物理 import 后,dts bundler 把 schemastery 类型内联进本地 declaration chunk,`lib/` 里既无 `#schemastery` 也无裸名——运行期与类型面双双自包含。
63
+ - 升级 vendored 版本是**行为变更**(settings/config 全表面):须跑全量单测 + 4999 第一人称后方可发版(步骤见 `third_party/README.md`)。
@@ -0,0 +1,82 @@
1
+ # better-dsh 归零 npm 运行时依赖 — 实测报告
2
+
3
+ 日期:2026-10-03 | 规格:`docs/specs/zero-npm-runtime-deps/spec.md` | 目标版本:`0.2.5-a`(本地过程版本,未发布)
4
+
5
+ ## 0. 一句话
6
+
7
+ better-dsh 的 `dependencies` / `optionalDependencies` / `postinstall` **全部为空**(60 条 `peerDependencies` 契约保留);真实安装器(DSH Plugin Manager 底层 pnpm)安装本插件现在只加 **1 个包**;REPL 的原生 ZMQ 依赖被换成 venv 内的 Python bridge;AST 的原生 `.node` 与 browser 的 puppeteer 由**我们自己的代码**在首次使用时自装/内联;任何一项失败只让该组件不可用。
8
+
9
+ ## 1. 起因(user 报告的真实现象)
10
+
11
+ DSH UI Plugin Manager 安装 better-dsh 时卡住并显示:
12
+
13
+ ```
14
+ + better-dsh 0.2.5-a
15
+ Packages: +15 -71
16
+ node_modules/zeromq install$ node ./script/install.js ← 我们的 REPL 原生依赖(cmake 构建)
17
+ Done in 3.7s using pnpm v11.7.0
18
+ [WARN] GET …/@oven/bun-linux-aarch64-android/-/…-1.4.2.tgz error (UND_ERR_SOCKET). Will retry in 10 seconds.
19
+ dsh: pnpm printed nothing for 600000ms and was terminated
20
+ ```
21
+
22
+ 定性:**不是构建错误,是超时**。Plugin Manager 对每条捕获输出的 pnpm 运行挂 600000 ms 静默上限(`packages/boot/plugin-manager/src/index.ts:184`),超时杀树并打印 `operations.ts:453` 那句。卡住的下载(Android/arm64 的 Bun 二进制)来自**同一 profile 里另一个插件**(`super-dsh@0.1.9 → bun@1.4.2`):插件 profile 是一个共享 pnpm 工程,"装插件 X" = "重解整个 profile",于是**别人的安装步骤能拖死我们的安装**。user 裁决:不再 declare 任何运行时依赖、不要 postinstall、不碰 npm/pnpm,二进制由组件自己按需自装。
23
+
24
+ ## 2. 改动清单
25
+
26
+ | 组件 | 原依赖 | 现处置 |
27
+ |---|---|---|
28
+ | hashline(`diff`/`file-type`/`xxhash-wasm`) | npm 依赖 | `tsdown noExternal` 内联进 `lib/`(`file-type` 经 A/B 构建证明在改动前就已被 tree-shake,直接删) |
29
+ | client(`use-sync-external-store`) | npm 依赖 | 早已内联进 `lib/client/index.js`,删除声明 |
30
+ | REPL(`zeromq`) | npm 依赖 + **顶层静态 import**(缺原生模块 → **整个插件加载死**) | 声明删除;新增 `src/kernel-bridge.py`(venv 内 pyzmq/jupyter_client,stdio JSON lines)+ `src/kernel-transport.ts` |
31
+ | browser(`puppeteer-core`) | npm 依赖 | 构建期内联(实测可拉起 Chrome);保留 vendor 自装兜底 |
32
+ | AST(`@oh-my-pi/pi-natives-*`) | 5 条 `optionalDependencies` | 声明删除;新增 `src/vendor.ts`,首次使用时自装到 `<packageRoot>/.vendor` |
33
+
34
+ 新增:`src/vendor.ts`(零依赖自装器:registry packument → sha512/shasum 校验 → 自写 tar+pax 解包 → `createRequire` 解析入口 → 递归闭包 + `os/cpu/libc` 平台过滤 → staging+rename 原子落盘)、`scripts/copy-kernel-bridge.mjs`(把 .py 拷进 `lib/`)。**全程无 `child_process`、无 npm/pnpm/npx 调用**。
35
+
36
+ 供给触发点:**守护进程 spin-up 自检**(原有,fire-and-forget)+ **首次使用 lazy**(`ensurePiNatives()` / `loadPuppeteer()` / venv lazy)。无 postinstall。
37
+
38
+ ## 3. 实测证据
39
+
40
+ ### 3.1 安装(真实安装器,rig profile)
41
+
42
+ ```
43
+ web Packages: +1 Done in 1.8s using pnpm v11.7.0
44
+ headless Packages: +1 Done in 858ms using pnpm v11.7.0
45
+ ```
46
+
47
+ 安装前后 profile 物理树零 `zeromq` / 零 `@oh-my-pi` / 零 `puppeteer`;插件照常加载,启动日志零错误,composed tree 含 `dashr-repl`。**这同时证明 fail-open:REPL 的原生依赖不在场,插件不再跟着死。**
48
+
49
+ ### 3.2 REPL(新传输层)
50
+
51
+ - 直连冒烟(真实 ipykernel):kernel_info ✅ / `print("SMOKE",6*7)` → `"SMOKE 42\n"` 流式 ✅ / 到 idle ✅ / **KERNEL_BOOTSTRAP 单元执行无错** ✅ / interrupt ✅ / dispose ✅。
52
+ - host-request comm(新传输唯一未覆盖路径):内核 `create_comm('dashr.host')` → iopub → Node → **control 通道** `comm_msg` 回包 → 内核 control-thread handler 解 future;cell 打印 `HOSTREPLY {'pong': True, ...}` ✅。
53
+ - **4999 第一人称**(`dsh headless` 真实 session):
54
+ - `eval` → `REPL_OK 42`;
55
+ - 同一 Python session 内 `await tool.bash({...})` → `HOST_OK\n`(宿主参数校验失败路径亦被触发)。
56
+
57
+ ### 3.3 AST(自装器)
58
+
59
+ - 自装器单测:provision `@oh-my-pi/pi-natives-linux-x64@18.0.6`(2×163MB `.node`)→ **dlopen 成功**(exports `astGrep`/`astEdit`/…)→ 二次调用 reuse → `^18.0.0 -> 18.4.12`。
60
+ - **4999 第一人称(最终产物)**:`dsh headless` 让 agent 用 `dvc://ast_grep` 搜 `loadPiNatives` → `totalMatches: 3 / filesWithMatches: 2 / filesSearched: 189`;同一时刻 `<plugin>/.vendor` 出现 **314MB** 的 `@oh-my-pi/pi-natives-linux-x64`,而 **profile 树里没有 `@oh-my-pi`** —— 证明二进制是我们自己下的。
61
+
62
+ ### 3.4 browser(puppeteer 内联)
63
+
64
+ `lib/puppeteer-core-*.js` 导出 206 项(含 `launch`/`connect`);用系统 Chrome 实测 `launch()` → `setContent` → `evaluate` 取回 `BUNDLED_PUPPETEER_OK`。
65
+
66
+ ### 3.5 构建卫生
67
+
68
+ `tsc --noEmit` 0 错;`npm run build` 通过;产物除 peer 契约外无外部包引用(`zeromq` 彻底消失);全量单测 **57 files / 649 passed, 1 skipped**。
69
+
70
+ ## 4. 过程中修掉的两个真 bug(都是本轮暴露、本轮修复)
71
+
72
+ 1. **bridge 静默死通道**:jupyter_client 反序列化后的 header `date` 是 `datetime`,`json.dumps` 抛异常且**在 pump 线程里静默打死整条通道**(现象:"桥 ready 了但一条消息都没有")。修法:`default=str` + 单条消息失败不许杀通道。
73
+ 2. **自装器锚点逃逸**:`findPackageDir(name, VENDOR_ROOT)` 会从 `.vendor` **向上走出**到 `node_modules`,破坏加载器"锚点有界"契约(单测 `ast-device.spec.ts` 抓到)。修法:vendor 走精确路径 `VENDOR_ROOT/node_modules/<name>`,且显式 `fromDir` 探测时**永不 provision**。
74
+ 另修正 round 2 我引入的 `package.json` **重复 `puppeteer-core` 键**(已在清空依赖块时一并消除)。
75
+
76
+ ## 5. 未做 / 已知限制
77
+
78
+ - **未发布**。按本仓红线,`npm publish` 需 user 单次明确放行。
79
+ - 自装代码自担:版本钉死、sha512 校验、平台探测、并发去重(当前无锁:两个进程同时首用可能重复下载,后者被 rename 覆盖)、代理/离线行为。
80
+ - AST 首次使用要下 ~314MB(上游包内含 baseline+modern 两个 CPU 变体,无法只取其一)。
81
+ - `.vendor`/`.venv-kernel` 都在包目录内,**重装插件会重下**(与既有 venv 行为一致)。
82
+ - 本机 rig 树里残留的旧 `@puppeteer`/`@oh-my-pi` 目录已手工清除(pnpm hoisted 树不回收无引用目录)。