better-dsh 0.2.3-d → 0.2.3-f

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 (18) hide show
  1. package/docs/50_test-reports/2026-09-11-control-prompt-into-eval-description/345/256/236/346/265/213/346/212/245/345/221/212.md +222 -0
  2. package/docs/50_test-reports/2026-09-12-fs-scheme-resolution-/345/256/236/346/265/213/346/212/245/345/221/212.md +81 -0
  3. package/docs/50_test-reports/2026-09-12-url-schemes-grammar-matrix/344/270/216catalog-centralize-/345/256/236/346/265/213/346/212/245/345/221/212.md +234 -0
  4. package/docs/50_test-reports/2026-09-12-url-schemes-recallable-context-design/351/252/214/350/257/201/346/212/245/345/221/212.md +160 -0
  5. package/docs/50_test-reports/2026-09-12-url-schemes-recallable-context-/345/256/236/346/265/213/345/211/247/346/234/254.md +44 -0
  6. package/docs/50_test-reports/2026-09-12-url-schemes-recallable-context-/345/256/236/346/265/213/346/212/245/345/221/212.md +89 -0
  7. package/docs/50_test-reports/2026-09-12-url-schemes-/345/205/255scheme/345/206/222/347/203/237/344/270/216/350/276/271/347/225/214/345/256/236/346/265/213/346/212/245/345/221/212.md +198 -0
  8. package/docs/50_test-reports/2026-09-13-hashline-off/344/270/213scheme/345/217/257/350/276/276/346/200/247/345/267/245/345/205/267/351/235/242/344/270/215/345/257/271/347/247/260-/345/256/236/346/265/213/346/212/245/345/221/212.md +246 -0
  9. package/docs/50_test-reports/2026-09-13-preact-ui-shell/345/256/236/346/265/213/346/212/245/345/221/212.md +50 -0
  10. package/docs/50_test-reports/v0.2.3c-mobile-wave/345/256/236/346/265/213/346/212/245/345/221/212.md +47 -0
  11. package/lib/client/index.js +26 -34
  12. package/lib/fs-aware/sandbox-plugin.d.ts +71 -0
  13. package/lib/fs-aware/sandbox-plugin.js +249 -0
  14. package/lib/index.d.ts +9 -0
  15. package/lib/index.js +547 -705
  16. package/lib/wrap-DC8O3SYz.js +721 -0
  17. package/package.json +6 -1
  18. package/url-schemes-instruction.md +22 -0
@@ -0,0 +1,222 @@
1
+ # control-prompt 并入 eval description — 第一人称实测报告
2
+
3
+ - 日期:2026-09-11
4
+ - change:`openspec/changes/2026-09-11-control-prompt-into-eval-description/`(design D1–D4)
5
+ - 被测运行时:**本 agent 自己所在的活体实例** —— `dsh web` @ `127.0.0.1:4989`,`DSH_HOME=/home/u1/workspaces/dashr/.dsh-test-4989`,profile `web` bundles = `[@deepseek-ai/dsh-base, @deepseek-ai/dsh-web-app, better-dsh]`,host 源码 = `upstream/deepseek-harness`(0.1.5-rc.2 线),插件来自 workspace 成员 `packages/better-dsh/better-dsh`(= canonical `./dashr` 的 rsync 副本,二者 src 已核为同步)。
6
+ - 端口说明:design 迁移计划写作 4999,实际 4997/4998/4999 已被 superd PoC 占用,本轮活体为 **4989**(`PORT=4989 bash test/start-4999.sh`,systemd 单元已起,日志 `.scratch/dsh-4989.log`)。
7
+ - 方法:**第一人称实测**——本 agent 的系统提示、wire 工具表、`eval` cell 桥接全部取自这个正在运行的 runtime;wire 侧数据从本会话落盘的 `session.v3.jsonl.zstd`(`request/header` / `system/message` 事件)读取,不由人手转写。
8
+
9
+ ---
10
+
11
+ ## 0. 结论
12
+
13
+ | 判定项 | 结果 |
14
+ |---|---|
15
+ | 两 section 是否消失、指引是否单源进 `eval` description | ✅ 通过(活体 wire 证据) |
16
+ | cell 内桥接调用(read/grep/bash/glob + delegation) | ✅ 通过(真实调用、返回可读) |
17
+ | 非 flat 名直调路径 / masked 8 名路由回文 / `dir(tool)` 内省 | ✅ 通过(无回归) |
18
+ | D1 示例键名修正 | ✅ 文档层正确;**行为层非修复**(旧键 `file_path` 仍被别名接受,见 §5.1) |
19
+ | 单测 / 类型无新增回归 | ✅ 458 passed / 14 failed(与 pristine HEAD 基线同数);tsc 13 = 基线 13 |
20
+ | 提示面预算 | ✅ 本 runtime 实测 −8,759 chars(−52.6% 等效),Σ description +1,965 |
21
+ | **发布面完整性** | ❌→✅ **发现发布阻断缺陷并已修复复验**:`package.json` `files` 未随文件更名更新,`npm pack` 产物不含 `eval-description.md` → 插件模块期 `readFileSync` 必 ENOENT(§5.2) |
22
+
23
+ **一句话**:change 在运行时行为上完全落地且无功能回归;但**原样发版会把插件打成一个加载即死的包**,已就地修好 `files` 并重建 canonical 产物复验。
24
+
25
+ ---
26
+
27
+ ## 1. 被测对象与构建事实
28
+
29
+ | 项 | 值 |
30
+ |---|---|
31
+ | 插件版本 | `dashr/package.json` = `better-dsh@0.2.3-c`(未 bump,任务 4.2 待做) |
32
+ | 活体副本产物 | `upstream/deepseek-harness/packages/better-dsh/better-dsh/lib/index.js` 内含 `readFileSync(new URL("../eval-description.md", import.meta.url), "utf8")`(11052 行) |
33
+ | 指引文件 | `dashr/eval-description.md` = 2,092 chars;与副本同名文件 **字节相同** |
34
+ | canonical 产物 | 本轮用 `tsdown` + `tsx scripts/build-client.ts` 重建:`lib/index.js` 440.07 kB、`lib/py-sdk.js` 0.10 kB、`lib/py-sdk.d.ts` 1.00 kB(渲染器拆除后的瘦身态)、`lib/client/index.js` 18.52 kB |
35
+ | 前状态的坑 | 修复前 canonical `dashr/lib` 是 **Sep 11 04:01 的改前产物**(内含 `control-prompt.md` 引用 1 处、`eval-description.md` 0 处),而 `dashr/control-prompt.md` 已删除 → 那个时点打包出来的是「lib 指旧文件 + 包内无新文件」的双坏包(§5.2) |
36
+
37
+ ---
38
+
39
+ ## 2. 静态契约核验
40
+
41
+ 1. **两 section 注销**:`dashr/src/*.ts` 已无 `systemPrompt.section` 注册,`dashr:control-prompt` / `dashr:tool-catalog` 仅剩两处**历史注释**(`src/index.ts:242`、`src/py-sdk.ts:5`)——无注册、无渲染物。
42
+ 2. **接线单源**:`EVAL_DESCRIPTION`(`src/index.ts:244`)= `readFileSync(new URL('../eval-description.md', import.meta.url), 'utf8')`;注册点 `src/index.ts:535` `description: EVAL_DESCRIPTION`;参数级 `EVAL_DESCRIPTION_PARAM_DESCRIPTION`(`:256` → `:541`)保留。
43
+ 3. **wire 参数 schema 未动**:`eval.parameters.properties` = `['cell','description','timeout','reset']`(与 design 一致)。
44
+ 4. **渲染器已删、策略保留**:`renderReplBridgeInstructions` / `renderToolsSdkPy` / `collectSdkSchemas` 在 `src/`+`test/` 零命中(仅注释追述);`isFlatBindableName` 仍在 `src/py-sdk.ts:35`,被 `src/index.ts:103/890`(绑定安装器)调用。
45
+ 5. **单测**:
46
+ - 三个被改 spec:`presentation.spec.ts` + `py-sdk.spec.ts` + `surface-devices/surface.spec.ts` → **32 passed / 3 files passed**(presentation 20 + surface 10 + py-sdk 2),与 tasks 3.1 记录一致。
47
+ - 全量:**Tests 14 failed | 458 passed (472)**;失败集中在 `agent-family.spec.ts` / `url-schema.spec.ts`(`SessionSeq is not a function`)。
48
+ - **独立基线对照(本轮新做,非引用任务记录)**:`git worktree add --detach /tmp/dashr-head HEAD` 拉 pristine HEAD(改前)→ 同两文件 **14 failed | 41 passed (55)**,`tsc --noEmit` = **13 error TS**;改动树 `tsc --noEmit` 同为 **13**。→ 14 失败与 13 类型错误均为既有基线,**零新增**。
49
+ 6. **D1 漂移护栏在位**:`presentation.spec.ts` 断言「注册 description `toBe(packaged md)`」+「description 中所有 `"key":` 引用的键 ∈ 相关工具 wire properties ∪ eval 自身四参」。
50
+
51
+ ---
52
+
53
+ ## 3. 活体第一人称矩阵(design 迁移计划 a–e)
54
+
55
+ ### (a) 新 session 无两 section、`eval.description` = Markdown 全文 ✅
56
+
57
+ 从本会话落盘的 wire 证据(非人工转写):
58
+
59
+ ```
60
+ system prompt chars = 7894
61
+ mentions dashr:control-prompt: False mentions dashr:tool-catalog: False
62
+ mentions 'TWO ways to act' : False mentions 'Calling tools from the scripting pad': False
63
+ mentions 'tool.<name>' 归因段 : False system prompt 内 'REPL' 出现次数: 0
64
+ wire eval.description chars = 2092
65
+ byte-identical to dashr/eval-description.md : True
66
+ byte-identical to monorepo 副本 eval-description.md : True
67
+ ```
68
+
69
+ description 内六段解剖全部命中:首句定义("Run one Python cell …")、参数语义("top-level `return` is a SyntaxError")、直调 vs cell 判据("Payload-shaped work…")、非 flat 例外("not plain identifiers")、`subagent` 别名注记("`subagent` is its native alias")、末尾桥接段("ONE positional argument" + "`true`/`false`/`null` become `True`/`False`/`None`" + "ToolCallError");**输出侧零陈述**(`not guaranteed` / `trial` / `试错` 等一律 0 命中)——符合 R3 user 裁决。
70
+
71
+ ### (b) cell 内真实桥接调用、返回值可读 ✅
72
+
73
+ 单个 cell 内实跑(真实 host 调用,非 mock):
74
+
75
+ ```json
76
+ {"read_path_ok": true, "read_head": "yY8│# Better Dsh — Dashr",
77
+ "grep_matches": 3,
78
+ "bash_stdout": "bridge-ok\n/home/u1/workspaces/dashr",
79
+ "glob_paths": ["upstream/deepseek-harness/packages/better-dsh/better-dsh/eval-description.md",
80
+ "dashr/eval-description.md"],
81
+ "glob_shape": ["paths", "root"],
82
+ "dir_tool_count": 25}
83
+ ```
84
+
85
+ delegation 桥端到端(真起了子 agent,回收两轮回执):
86
+
87
+ ```
88
+ tool.agent({"description":"bridge smoke","prompt":"Reply with exactly: bridge-alive","run_in_background":True})
89
+ → {'kind': 'continuable', 'subagentId': '55661884-…'}
90
+ ← child 结束语: bridge-alive
91
+ tool.agent_message({"receiver":"child","subagent_id":"55661884-…","message":"Reply with exactly: bridge-alive-2"})
92
+ → {'messageId': 'd0e77672-…'}
93
+ ← child 结束语: bridge-alive-2
94
+ ```
95
+
96
+ `web_search` / `job_list` 桥同样返回结构化可读值。cell 语义三断言实测:跨 cell 变量存活(前 cell 的 `masked`/`sid` 在后 cell 可见);`compile("return 5","<cell>","exec")` → `SyntaxError: 'return' outside function`;kwargs 形 `tool.read(path=…)` → `TypeError: dashr binding calls do not accept keyword arguments`;`reset=True` 后 `masked`/`sid`/`marker` 全部消失而桥接照常可用。
97
+
98
+ ### (c) 非 flat 名直调路径不回归 ✅(本 scope 无真名,用等价物证)
99
+
100
+ 本 runtime wire 26 名**全部是 flat 标识符**,非 flat 分支无真实样本。等价验证:
101
+
102
+ ```
103
+ getattr(tool, "mcp__demo-server")({}) → ToolCallError: unknown binding "tool.mcp__demo-server"
104
+ `tool.mcp__demo-server(...)` 属性语法 → Python SyntaxError(语言层就不可写)
105
+ ```
106
+
107
+ 即「非 flat 名没有 `tool.<name>` 成员、必须直调」的契约成立;`isFlatBindableName` 本身由 `py-sdk.spec.ts` 单测覆盖(2 tests 绿)。**声明**:真实带连字符/`__` 的 MCP 工具在场的端到端用例本轮未覆盖(本 runtime 无 MCP 工具),见 §6。
108
+
109
+ ### (d) `read` 示例键名 `path` 正确 ✅(并发现旧键仍可用,见 §5.1)
110
+
111
+ ```
112
+ tool.read({"path": "README.md", "limit": 3}) → 成功('"yY8│# Better Dsh — Dashr"' 等 hash 行)
113
+ tool.read({"limit": 2}) → ToolCallError(path 缺失必错)
114
+ tool.read({"file_path": "README.md", "limit": 2}) → **也成功,且返回值与 path 版完全一致**
115
+ dir(tool) 内 25 名 = wire 26 名 − eval (与 `_dashr_install_bindings` 注入的 25 名逐一吻合)
116
+ ```
117
+
118
+ ### (e) masked 8 名 / 别名 / 内省回归 ✅
119
+
120
+ - masked 8 名(`skill,send_message,report,list_agents,subagent_fork,interrupt_agent,workflow,ralph`)在 **wire 26 名中全缺席**,在 `dir(tool)` 中亦不在绑定清单;`tool.skill({})` → `ToolCallError`,`str(e) == 'unknown binding "tool.skill"'`,`.toolName == 'skill'`(契约字段健在),与瞎编名 `tool.definitely_not_a_tool` 的**模型可见回文完全同形** → 同一条 route-back 路。
121
+ - `agent`/`agent_message`/`subagent`/`agent_workflow` 桥正常(§3b);`dir(tool)` 内省面 25 名不变。
122
+ - `dashr:escalation-guidance`(order 116)不在本 change 范围,本轮实测该 context 仍以 runtime-context 段落出现(升级指引文案可读),未受影响。
123
+
124
+ ---
125
+
126
+ ## 4. 提示面预算复测(task 3.4)
127
+
128
+ **基线取法(同 harness 家族、可复算)**:`.dsh-test` 里 2026-09-06 的旧 session(`session-851d54da…`,wire 25 工具、`eval` 591 chars)留有完整 `system/message` + `request/header`;与本活体的会话记录做 A/B。
129
+
130
+ | 指标 | 改前(Sep 06 基线) | 改后(本活体 2026-09-11) | Δ |
131
+ |---|---|---|---|
132
+ | base system prompt | 14,709 chars | **7,894 chars** | **−6,815(−46.3%)** |
133
+ | 其中 `## The DASHR REPL interface`(control prompt) | 4,127 chars | 0 | −4,127 |
134
+ | 其中 `## Calling tools from the scripting pad` + 27 行 `tool.x(args: …) -> …` 声明块 | 4,632 chars | 0 | −4,632 |
135
+ | 两 section 合计 | 8,759 chars | 0 | **−8,759** |
136
+ | 残余漂移(非本 change) | — | — | **+1,944** |
137
+ | wire Σ description | 9,726(25 工具) | **11,691(26 工具)** | +1,965(+20.2%) |
138
+ | 其中 `eval` 单工具 | 591 | **2,092** | **+1,501** |
139
+ | 其中新增工具 `present` | — | 464 | +464 |
140
+
141
+ **残余 +1,944 已定位且与本 change 无关**:对「去掉两 section、去掉 runtime-context 段落(checkout/GUI/cwd/model 四段,位置与端口不同不算内容)」后的**核心**提示做行级 diff,得出**纯插入 19 行**、且校验 `基线核心 + 插入行 == 当前核心` 为 True——插入内容全部是 v0.2.3-b(09-08)hashline content-locator 带来的 `read`/`edit` HASH 锚点指引段。即:**本 change 的作用就是把两份 section(−8,759)从提示面移除,别处一字未动**。
142
+ ⇒ 若只看本 change 对今日 runtime 的净影响:`7,894 + 8,759 = 16,653 → 7,894`(**−52.6%**)。
143
+
144
+ 与 design 预估(prod 43 工具:17,838 → ~10,850;Σ 14,201 → ~15,800)方向一致,绝对值不可直接比(本 runtime 是 26 工具的 web profile,scope 不同);design 对 `eval` 描述长度的目标 ~2,200 chars,实测 **2,092**,达标。
145
+
146
+ ---
147
+
148
+ ## 5. 发现与处置
149
+
150
+ ### 5.1 D1「示例键名修正」是文档准确性问题,不是行为修复(低)
151
+
152
+ `dashr/eval-description.md` 把示例从 `file_path` 改成 `path`,与 wire schema(`read.parameters` = `path/offset/limit`)一致——**正确**。但实测旧键**并未失效**:vendored hashline 的 `normalizeRequest` 显式做 `file_path → path` 别名(`src/url-schema/vendored/hashline/contract.js:74` 一带)。结论:
153
+ - 旧 control prompt 的 `file_path` 示例属于「与 wire schema 不符但侥幸可用」;
154
+ - 新示例消除了这层侥幸(若上游/我们日后收紧 `additionalProperties`,旧写法即死);
155
+ - 报告据此把 D1 定性为**文档正确性修正**,而非「修了一个报错」。建议(不改本轮结论):如果确实希望旧键成为硬错,需要单独 change 去掉该别名,本 change 不应背这个期望。
156
+
157
+ ### 5.2 【发布阻断,已修复并复验】`package.json` `files` 未随文件更名更新
158
+
159
+ **症状**:`dashr/package.json` `files` 仍写 `"control-prompt.md"`(该文件已删除),且**没有** `"eval-description.md"`。
160
+
161
+ **证据链**:
162
+ 1. 修复前 `npm pack --dry-run`(`--cache /tmp/npmcache`,因 `~/.npm` 只读)→ 69 个文件,`eval-description.md` **不在**产物中,`control-prompt.md` 也不在(已被删)。
163
+ 2. 而插件在**模块期**就执行 `readFileSync(new URL('../eval-description.md', import.meta.url), 'utf8')`(`lib/index.js:11052`),路径解析到**包根**。
164
+ 3. 用修复前语义复现:从真实 tarball 解包后把该文件移走 → `ENOENT: no such file or directory, open '/tmp/pkgcheck/package/eval-description.md'`。
165
+ ⇒ 部署态后果不是「指引缺失」而是 **`better-dsh` 整个插件加载即抛错**(挂载失败,插件全部功能死);而 4989 活体因跑源码树(文件在盘上)完全看不到这个问题——典型的「活体绿、发版死」。
166
+ 4. 附带事实:修复前 canonical `dashr/lib` 还是 04:01 的**改前产物**(仍引用 `control-prompt.md`,而该文件已删),故那一时点即使补上 `files`,lib 侧依旧指旧文件——两个坑叠加。
167
+
168
+ **处置**:
169
+ - 改 `dashr/package.json`:`files` 中 `"control-prompt.md"` → `"eval-description.md"`(1 行)。
170
+ - 重建 canonical 产物:`tsdown`(8 files / 495.94 kB,`py-sdk.d.ts` 瘦到 1.00 kB)+ `tsx scripts/build-client.ts`(18.52 kB)。
171
+ - 复验:`npm pack`(真实打包)→ **70 files**,根层含 `eval-description.md`;解包后 `lib/index.js` 对 `control-prompt.md` 引用 0、对 `eval-description.md` 引用 1;按**模块期同款 URL** 读取成功,`chars = 2092`,首行 = `Run one Python cell on a session-persistent scripting pad. …`。
172
+
173
+ **残留提醒(未改,交 user/后续任务裁决)**:
174
+ - 版本号与 lock 仍 `0.2.3-c`、AGENTS ✅ 条目与 tasks 3.3/3.4/4.1 未回填(本轮只做验证与报告,不做发布动作);
175
+ - `openspec/…/tasks.md` 的 3.2 记录「canonical 未重建、4999 挂起」与现状已不符(活体已在 4989 跑起来),建议收口时同步。
176
+
177
+ ### 5.3 小瑕疵(不影响判定)
178
+
179
+ - `src/index.ts:242`、`src/py-sdk.ts:5` 的注释仍以「former section」口吻提到 `dashr:control-prompt`/`dashr:tool-catalog`——tasks 2.1 的「`grep src/` 零命中」严格说不成立(是注释,不是注册)。可在收口时顺手改写以免后人误解为「还在」。
180
+ - `eval-description.md` 示例路径 `docs/README.md` 在 dashr 仓库**不存在**(README 在根;`upstream/deepseek-harness/docs/README.md` 也没有)。示例属通用示意(与上游 `eval` 文档同构),但既然本 change 以「消灭键名漂移」为由加了单测护栏,路径同样可以选一个真实存在的(如 `README.md`)以免带偏模型。**注**:这正是本轮第一次 cell 试跑踩到的 `E_NOT_FOUND`。
181
+
182
+ ---
183
+
184
+ ## 6. 未覆盖 / 残留风险
185
+
186
+ 1. **非 flat 名的端到端**:本 runtime 无 MCP/带连字符工具,只有「无成员 + 属性语法不可写 + 同名 route-back」的等价证据与单测覆盖(§3c)。若验收要求真名,需挂一个 MCP 工具再跑一次。
187
+ 2. **prompt 前缀缓存一次性失效(design D4 自述 BREAKING)**:本轮未量化既有会话的 cache 命中变化(无 provider 侧计数入口)。
188
+ 3. **prod 侧未验证**:本报告全部结论来自 4989 源码级实例;prod 3080 仍是发布态 `better-dsh@0.2.3-c`(未装本 change),按 AGENTS §〇 需 user 单次确认后才发布、再以普通 user 身份装机实测。
189
+ 4. **未跑 `pnpm run build` 全量**(仅 `tsdown` + `build-client` + `tsc` + vitest);monorepo 侧本轮未重建(活体所用产物在此之前已由 change 作者构建并处于在位状态,本轮只核了它的字节行为)。
190
+
191
+ ---
192
+
193
+ ## 7. 发布闸门状态(AGENTS §〇)
194
+
195
+ | 闸 | 状态 |
196
+ |---|---|
197
+ | a. 第一人称实测通过 | ✅ 本报告 §3(同一 runtime 上跑通改动路径;且本 agent 自身即运行在被改的提示面上) |
198
+ | b. 报告落盘 `docs/50_test-reports/` | ✅ 本文件 |
199
+ | c. user 单次明确确认放行 | ⏳ **未取得** |
200
+ | d. 前置修复(`files` 缺口 + canonical 产物重建) | ✅ 已做并复验(§5.2) |
201
+
202
+ ⇒ **不具备发布条件**,等 user 明确放行;本报告不构成发布授权。
203
+
204
+ ---
205
+
206
+ ## 附录 A — 原始证据位置
207
+
208
+ | 证据 | 位置/取法 |
209
+ |---|---|
210
+ | 本会话 wire 快照(`system/message`、`request/header`) | `.dsh-test-4989/sessions/--home-u1-workspaces-dashr--/session-21747443-752d-4647-9208-c929a635c998/session.v3.jsonl.zstd`(`zstd -dc` 后按 `type` 取事件) |
211
+ | 改前基线 session | `.dsh-test/sessions/--home-u1-workspaces-temp--/session-851d54da-bffa-4c44-83db-c3991708e3ab/session.v3.jsonl.zstd`(2026-09-06,25 工具 / eval 591) |
212
+ | pristine HEAD 基线(单测+tsc) | `git worktree add --detach /tmp/dashr-head HEAD`(HEAD=`f69528f`)→ 14 failed/41 passed(两 spec)、tsc 13 |
213
+ | 打包复验 | `npm pack --cache /tmp/npmcache --pack-destination /tmp/pkgcheck2` → `/tmp/pkgcheck2/better-dsh-0.2.3-c.tgz`(70 files,含包根 `eval-description.md`) |
214
+ | 活体 cell 记录 | 本 session transcript 的 `tool/call`/`tool/result`(eval × 6,含 delegation 往返) |
215
+
216
+ ### 附 A.1 本报告触发的一次实际改动
217
+
218
+ ```
219
+ dashr/package.json: files: ["lib","docs","cordis.patch.yml","eval-description.md","scripts/kernel-provision.mjs"]
220
+ (原 "control-prompt.md")
221
+ 重建:dashr/lib/*(tsdown 8 files 495.94 kB;client 18.52 kB)
222
+ ```
@@ -0,0 +1,81 @@
1
+ # fs-scheme-resolution — 实测报告(change 2026-09-12-fs-scheme-resolution)
2
+
3
+ > 状态:**代码 + 单测 + 活体自测完成**;npm publish 未启动(AGENTS §〇,等 user 单次确认)。
4
+
5
+ ## 1. 被测对象与身份锚定
6
+
7
+ | 项 | 值 |
8
+ |---|---|
9
+ | change | `2026-09-12-fs-scheme-resolution`(FS 层挂载 + 工具解绑——上一 change 的 Phase-2 尖兵兑现) |
10
+ | 实例 | `dsh-4999-test`(0.1.5-rc.2 + better-dsh monorepo 副本,home `.dsh-test`) |
11
+ | 挂载 | home 层 `.dsh-test/cordis.patch.yml`(新建):`{id: fs-sandbox, name: 'better-dsh/fs-aware-sandbox', config: {urlSchemes: true}}` + `{id: better-dsh, config: {hashline: false}}` |
12
+ | 关键裁决 | D2:ctx:///agent:// 为会话型 scheme,FS 层不解析(结构化 `CTX_SESSION_LAYER` 边界),工具层呈现分支继续服务 |
13
+ | 实测会话 | `session-1c6ebcab`(自驱,`.scratch/url-schemes-live-driver5.sh`) |
14
+
15
+ ## 2. 代码交付
16
+
17
+ - `src/fs-aware/sandbox-plugin.ts`:`FsAwareSandboxFileSystem extends SandboxedFileSystem`(**静态**继承——替换行下沙箱包定义性在场;`fs` 服务名由 FileSystem 基类 `super(ctx, 'fs')` 烙定);override resolve/stat/readText(scheme → UrlResolver 解引用;ctx://、agent:// → `CTX_SESSION_LAYER`)+ writeText/editText(virtual → `FS_VIRTUAL_READONLY`);`urlSchemes: false` → 全 super 逐位 stock。FS 层 resolver = skill(ctx.skills + fs=this)/ dsh(ctx.settings + resolveDocsDir)/ dvc(模块级共享注册表)/ http。
18
+ - 构建面:tsdown 第二 entry → `lib/fs-aware/sandbox-plugin.js`;package.json `exports['./fs-aware-sandbox']`。
19
+
20
+ ## 3. 验证
21
+
22
+ | 层 | 结果 |
23
+ |---|---|
24
+ | 单测 `fs-aware.spec` | **5/5**:dsh://docs 虚拟解引用 + stat、CTX_SESSION_LAYER 边界(resolve/readText/agent://)、FS_VIRTUAL_READONLY(writeText×2)、真实路径透传、gate off stock 退化 |
25
+ | 全量 | vitest **487 passed / 14 failed**(基线同族,宿主 API 漂移);tsc **13 基线**;openspec **2/2**(新 change strict 过) |
26
+ | 活体(三轮自驱同法) | 见 §4 |
27
+
28
+ ## 4. 活体自测(hashline OFF = read 为零工具层参与的原生 read)
29
+
30
+ | # | 探针 | 观测 | 判定 |
31
+ |---|---|---|---|
32
+ | 1 | 原生 read `dsh://docs` | **8,929 chars 文档清单**(agent-lifecycle.md、AGENTS.md、api-gateway.md…) | ✅ **FS 闸口解析直达证据**——零工具层 scheme 代码参与 |
33
+ | 2 | 原生 read `dvc://` | 677 chars 设备表(ast_edit/ast_grep/browser/lsp) | ✅ 注册表共享成立 |
34
+ | 3 | 原生 read `ctx://session` 系 | 走工具层呈现分支(gates.urlSchemes on),未知 face → 结构化错误;`user_prompts[0]` 正常 | ✅ D2 边界:会话型留工具层;无 wrapper 的消费方则得 `CTX_SESSION_LAYER`(单测证明) |
35
+ | 4 | 原生 read `package.json`(path 参数) | isError=true(原生参数校验诚实失败),agent 自纠 `dashr/package.json` 成功 | ✅ 原生语义如实呈现 |
36
+ | 5 | 真实路径读取 | 正常(native 格式,无锚点——hashline off 的预期形态) | ✅ 真实路径零扰动 |
37
+ | 6 | agent 自发扩展 | eval/glob/bash 附加探针(50,520 chars 输出)全健康 | ✅ 无回归 |
38
+
39
+ 压缩链(`/compact`,13 items / 7,889 tok)与 session persistence 全程正常(FS 替换未扰动会话存储——其自持 fs 不经 ctx.fs,设计如此)。
40
+
41
+ ## 4b. 挂载机制的证伪与改道(重要工程记录)
42
+
43
+ 「home/profile 层同 id 行重述 fs-sandbox + name 重指」在 0.1.5-rc.2 上**不可用**:三种 name 形态(bare 子路径 / 带引号 bare / 相对路径)全部**静默回滚**为 stock(dump 与活体行为双证)。根因:boot 期行导入走 `loader.internal.import`(编译期 bun-registry,仅 @deepseek-ai/* 在册),非在册名替换导入失败 → `Entry.update` 回滚;upstream 根 symlink 亦不达(import 不走 Node 分支)。**采纳改道 = 实例级方法包装**(`src/fs-aware/wrap.ts`,doc §5.3 最后手段):包装活体 ctx.fs 的 5 个公开方法,urlSchemes 总闸 + symbol 幂等 + 随重启还原;子路径模块/exports 实验回退(D1b 详录 design.md)。已知代价:上游改这 5 个方法签名即碎(公开面,可控)。
44
+
45
+ > **gates 标签(2026-09-13 补)**:§4 活体于 `urlSchemes: true + hashline: false`(原生 read + FS 包装层)取证。
46
+
47
+ ## 4c. Gate on/off 变体 + LSP 成功路径(✅ 2026-09-12 深夜补测)
48
+
49
+ - **gate OFF 活体变体(task 2.4 ✅)**:双闸关闭(better-dsh `urlSchemes:false` + fs 行 false)重启后自驱实测——原生 read 把 `dsh://docs` 按相对路径折叠(`/home/u1/workspaces/dashr/dsh:/docs not found`,测试 agent 原话「只有常规的 not found…没有虚拟文件系统,也没有 scheme 解析失败之类的专门报错」),真实文件读取不受影响——**逐位等同 stock**,unit+live 双证。
50
+ - **LSP 成功路径(task 2.5 ✅)**:安装 typescript-language-server 6.0.0 + typescript@5.9(**坑**:TS 7.x 无 JS tsserver,t-ls 6.x 需 5.x;且 t-ls 6.0 无 `--tsserver-path` 选项,靠 workspace 根的 node_modules 发现——已装 `/home/u1/workspaces/dashr/node_modules/typescript`)。`write dvc://lsp {action:diagnostics, file:.scratch/lsp-probe.ts}` → server 拉起 → 结构化诊断**精准命中夹具故意埋的 TS2322 type error(line 8)**;同一探针的 wire 渲染含完整 payload JSON(§3.1 修复活体证实)。definition/references/hover/format 共享同一 LSP client,按需补测。
51
+ - 六 scheme 修复(§3.1/§3.3/§3.4)已随后续轮次活体证实:`?q=` 行过滤、glob path-backed 翻译、dvc payload 上 wire。
52
+
53
+ ## 4d. skill:// 重分类为会话层 scheme(✅ 2026-09-13 user 裁决 + 活体复验)
54
+
55
+ **裁决**:技能加载路径解析(CWD / user-level global / app 运行时根如 `.claw/skills`、`.hermes/skills`;扫描深度——DSH 只扫 `skills/` 下一层(`dir/SKILL.md` 或 `*.md`),superpowers/openspec 的二层嵌套扫不到,而 OMP 式 `**/skills/*/SKILL.md` 任意深度命中)**全部是宿主 `dsh-skill-filesystem` 的业务逻辑,插件不得自写一套近似版**。第一版补通道(`sessionCwd` 配置 + scopedAware list 启发式 + 错误信息建议 `.agents/skills/<name>/SKILL.md` 直读)整体拆除。
56
+
57
+ **证伪链**(推翻上一轮「P1-b 已修复」的活体结论):
58
+ 1. **dump-config(.dsh-test 活体)**:`dsh-web-app` bundle patch 在宿主层 **disable** 了 `skill-filesystem` 与 `tool-skill` 行 → web profile 的 **全局层没有任何 skill provider**,技能只在各 agent preset 的 scope 层可见(会话 catalog 可见书签级技能即是 preset 层工具的产物)。
59
+ 2. **离线 probe**(monorepo 同组合):全局层 `list({cwd})` 11 项、`get('book-to-skill',{cwd})` OK——registry 本身无恙;4999 活体失败是部署组合(provider 只在 preset 层)所致,非「agent-scope 结构性不可见」。
60
+ 3. **4999 活体 A/B**(session 8b6dcf85):FS 层读 user-global 技能(`skill://markitdown`,**无需 cwd** 即应可见)与 project 技能(`skill://book-to-skill`)**双双 `unknown`** → sessionCwd 通道与启发式在真实部署下从未生效(全局层 list 同空)。
61
+
62
+ **最终形态**:`skill://` 与 `ctx://`/`agent://` 同族,FS 层一律 `CTX_SESSION_LAYER` 边界错误(指名原生 `skill` 工具为调用通道);`sessionCwd` 从 schema、wrap 线、profile patch 行整体移除;工具层 handler lookup 收敛为 `dsh-tool-skill` 精确镜像(cwd 只取 `agent.session.header.cwd`,scope = agent);原生 `skill` 工具解禁态即为全功能调用路径。
63
+
64
+ **活体复验(5+1 探针全过,2026-09-13)**:
65
+
66
+ | 探针 | 结果 |
67
+ |---|---|
68
+ | `read skill://book-to-skill`(FS 层) | ✅ 结构化边界错误,指名原生 skill 工具(session 37899b26) |
69
+ | `read skill://markitdown:1-5`(FS 层,user-global 名) | ✅ 同上,与技能名无关(session bfa99754) |
70
+ | 原生 `skill` 工具 `name=book-to-skill` | ✅ 完整 `<skill_content>` + resourceBase(session f12a206e) |
71
+ | `read https://example.com` | ✅ 正常抓取,P1-a 存活(session 60418790) |
72
+ | `read dsh://docs:1-3` | ✅ 选择器正常(session c742a81f) |
73
+ | `grep path=skill://book-to-skill pattern=^#` | ✅ 560 matches,工具层通道完好(session 029aa5bb) |
74
+
75
+ 回归:vitest 489 passed / 14 failed(agent-family 12 + url-schemes.spec 2,host-drift 基线族,零新增;总测试数 505→504 系删除一条已失效的 cwd-passthrough 测试);tsc 13(基线);openspec valid。构建:rsync → tsdown → build-client,`sessionCwd`/`URL_SKILL_NOT_INVOCABLE_SCOPE` 在 lib 双 chunk 0 命中。
76
+
77
+ ## 5. 边界与遗留
78
+
79
+ - `urlSchemes:false` 活体变体:单测已覆盖(gate off 全 super 矩阵),活体变体启动一轮并入下次批量验证(tasks 2.4 部分完成)。
80
+ - `FS_VIRTUAL_READONLY` 经工具 write 不可达(write wrapper 的 scheme 分发先行拦截——dvc 执行语义保留),该错误面由非 wrapper 消费方触发,单测覆盖。
81
+ - prod 采用:用户侧加同款 home patch 行(模板见 `.dsh-test/cordis.patch.yml`);npm publish 未启动。
@@ -0,0 +1,234 @@
1
+ # 2026-09-12 URL schemes 全矩阵语法实测 + scheme catalog 单一源化 — 实测报告
2
+
3
+ ## 〇、元信息
4
+
5
+ | 项 | 值 |
6
+ |---|---|
7
+ | 日期 | 2026-09-12 |
8
+ | 变更 | `url-schemes`: scheme 枚举单一源化(新增 `catalog.ts`)+ 全 scheme/工具/语法矩阵实测 |
9
+ | 实测形态 | **第一人称活体实测**:在运行中的 4999 实例里,用真实 agent session 经 `eval` pad 调 `tool.read/write/grep/glob` 逐条打靶(非静态走查、非单测代理) |
10
+ | 实测实例 | `http://127.0.0.1:4999`,session `session-114394eb-1dc6-4ee3-8f03-d863167c2528`,`DSH_HOME=/home/u1/workspaces/dashr/.dsh-test` |
11
+ | canonical 源 | `/home/u1/workspaces/dashr/dashr` |
12
+ | 验证副本 | `upstream/deepseek-harness/packages/better-dsh/better-dsh`(AGENTS.md §二 Dev/Test 1) |
13
+ | 基线 | 工作树处于 `url-schema/` → `url-schemes/` 重命名进行中(未提交),非本次变更所引入 |
14
+ | 发布状态 | **未发布、未构建、未重启**(见 §七) |
15
+
16
+ > 起因:user 问"哪些 scheme 能在 4 个工具里用",并特别要求核验 **device(`dvc://`)** 与 **`ctx://`** 是否各自遵循统一语法(一级/二级 subpath + 冒号 selector)。同时裁定把散落的 scheme 枚举**合并为单一源**并**结构化 surface、附示例**,以提高运行时大模型的可见度。
17
+
18
+ ---
19
+
20
+ ## 一、结论摘要
21
+
22
+ 1. **user 的假设基本成立但不完整**:5 个自有 scheme + `http(s)://` 在 `read`/`grep`/`glob` 三个工具中**全部可用**;但 **`write` 并非全 scheme 可用——只有 `dvc://` 可写**,其余 6 个 scheme token 一律结构化拒绝(`ctx://` 另有定制文案)。同一句"write 也支持 scheme://"不成立。
23
+ 2. **scheme 枚举点远多于"两个地方"**:实测清点为 **6 处**(`read.description`、`read.path.description`、`general-section.ts`、`write.ts` 的 `REGISTERED_SCHEMES`、`selector.ts` 的无 scheme 报错、以及 `resolver/index/grep/glob` 文档块)。这正是漂移的土壤。
24
+ 3. **`ctx://` 的缺席是"出生缺陷"**:`git log -S'skill/agent/dsh/dvc/ctx'` 无任何命中 ⇒ 自 `074b6ae`(v0.1.8c) 引入以来,`read` 的两处枚举**从未包含 `ctx://`**,且是唯一被系统提示词单列 bullet 的 scheme。已修复并加回归护栏。
25
+ 4. **`ctx://` 与 `dvc://` 各自"部分"遵循统一语法**:
26
+ - `ctx://` —— **不遵循**。它的元素寻址用 **方括号 `[n]` 且写在 path 里**,`selector.ts` 完全不认识;`/original` 是 path 后缀。它虽是 `selectorAware`,却**只接受 `:raw`/`:N-M`**,`:path/` 与 `?q=` 抛 `CTX_BAD_SELECTOR`。故"五个 scheme 共享一套 selector 语法"的文档断言**为假**。
27
+ - `dvc://` —— device 名只取**第一段**,`dvc://browser/sub` 的 `/sub` **被静默丢弃**;但它作为非 selectorAware handler,**接受**全部统一 selector。
28
+ 5. 发现 **13 项偏差/缺陷**(§五),其中 **3 项为功能性缺陷**(`dsh://docs` 号称 path-backed 实未实现,导致 `glob` 返回 0 与 JSON 碎片当路径)。
29
+ 6. 验证:副本全量 **483 测试 / 482 通过 / 1 失败**,该 1 失败为**改动前既存**的环境性失败(§六基线对照);我改动的文件 **tsc 0 error**。
30
+
31
+ ---
32
+
33
+ ## 二、改动清单
34
+
35
+ ### 新增
36
+
37
+ - `src/url-schemes/catalog.ts` —— **scheme 集的唯一真源**。`SCHEME_CATALOGUE`(每 scheme:`names/display/paths/purpose/example/write/selector`)+ 派生的 `SCHEME_NAMES`、`SCHEME_DISPLAYS`、`READ_ONLY_SCHEME_COUNT` + 两个渲染器 `renderGeneralSectionText()`、`renderExampleHint()`。**零依赖**(不 import `selector.ts`/handlers),故低层模块可安全反向引用而不成环。
38
+
39
+ ### 改为从单一源渲染
40
+
41
+ | 文件 | 改动 |
42
+ |---|---|
43
+ | `src/url-schemes/general-section.ts` | 删掉硬编码 `GENERAL_SECTION_TEXT`,改 `renderGeneralSectionText()` |
44
+ | `src/url-schemes/tools/read.ts` | 两处枚举(工具 description + `path` 参数)不再手写;`path` 示例由 `renderExampleHint()` 生成 |
45
+ | `src/url-schemes/tools/write.ts` | `const REGISTERED_SCHEMES = ['agent','ctx',...]` 字面量 → `SCHEME_NAMES` |
46
+ | `src/url-schemes/selector.ts` | `URL_NO_SCHEME` 报错文案改由 `SCHEME_DISPLAYS` 渲染 |
47
+ | `test/url-schemes/general-section.spec.ts` | 预算断言更新 + **新增"6 个 scheme token 必须全部出现"的回归护栏** |
48
+
49
+ ### 系统提示词预算
50
+
51
+ `url-schema:general` 段:**433 → 1280 chars(+847,约 +2.9×)**。这是本变更唯一显著的常驻开销,为 user 明示要求的"结构化 + 示例 + 提高可见度"所付;护栏断言上限设为 1400。
52
+
53
+ ---
54
+
55
+ ## 三、工具 × scheme 支持矩阵(活体实测)
56
+
57
+ `read` = 读取解析;`write` = scheme 写通道;`grep`/`glob` = URL 作 `path`/`pattern`。
58
+
59
+ | scheme | read | write | grep | glob | 备注 |
60
+ |---|---|---|---|---|---|
61
+ | `skill://` | ✗(目录为空) | ✗ `URL_WRITE_UNSUPPORTED` | ✗ | ✗ | 本 runtime 唯一完全不可用者 |
62
+ | `agent://` | ✓ | ✗ `URL_WRITE_UNSUPPORTED` | ✓ 物化 | ✓ 物化 | `/transcript` 要求 session **live** |
63
+ | `dsh://` | ✓ | ✗ `URL_WRITE_UNSUPPORTED` | ✓ 物化 | ✓ 物化 | 应走 path-backed,实际物化(缺陷 F3) |
64
+ | `ctx://` | ✓ | ✗ `URL_READ_ONLY`(定制文案) | ✓ 物化 | ✓ 物化 | 只读快照,语义正当 |
65
+ | `dvc://` | ✓ roster/doc | **✓ JSON dispatch** | ✓ 物化 | ✓ 物化 | **唯一可写** |
66
+ | `http(s)://` | ✓ | ✗ `URL_WRITE_UNSUPPORTED` | ✓ 物化 | ✓ 物化 | selector-exempt |
67
+
68
+ ### 实测原文摘录
69
+
70
+ ```
71
+ read skill:// ERR skill "" is unknown or no longer available
72
+ read dsh:// ERR dsh://: unknown resource "(empty)" — expected "docs" or "config"
73
+ read dsh://docs OK 8154B [
74
+ read dsh://config OK 2749B {
75
+ read ctx:// OK 594B ctx://session statistics snapshot …
76
+ read ctx://session OK 814B {
77
+ read ctx://session/transcript OK 122600B [0000009] USER
78
+ read ctx://session/compactions OK 25B (no compactions recorded)
79
+ read dvc:// OK 643B ast_edit… ast_grep… browser… lsp…
80
+ read dvc://browser OK 226B
81
+ read dvc://browser/sub OK 226B ← /sub 被静默忽略(F6)
82
+ read agent:// OK 9B no agents / roster header
83
+ read agent://nosuchagent/transcript ERR unknown agent … scoped to your family tree
84
+
85
+ write .scratch/url-scheme-probe.txt OK create
86
+ write skill:// / dsh:// / agent:// / http:// / https://
87
+ ERR URL_WRITE_UNSUPPORTED
88
+ write ctx://session ERR URL_READ_ONLY("curated read-only snapshot")
89
+ write dvc://nosuchdev ERR DVC_UNKNOWN_DEVICE(registered: ast_edit, ast_grep, browser, lsp)
90
+ write dvc://ast_grep (非 JSON) ERR DVC_BAD_ARGS
91
+ write dvc://ast_grep (绝对路径 JSON) OK {"matches":[…]}
92
+ write nosuchscheme://x ERR URL_UNREGISTERED_SCHEME
93
+ ```
94
+
95
+ ### `grep` / `glob` 实测原文摘录
96
+
97
+ ```
98
+ grep dsh://docs/subsystems/skills.md OK match.path = /dev/shm/dashr-url-*/content.txt ← 物化,非真实盘路径
99
+ glob pattern="dsh://docs" OK paths = ['[', ' "agent-lifecycle.md",', …] ← JSON 碎片当路径(F4)
100
+ glob pattern="**/*skills.md" path="dsh://docs" OK paths = [] ← 真实目录本应命中(F3)
101
+ ```
102
+
103
+ ---
104
+
105
+ ## 四、语法一致性实测(重点:`ctx://` 与 `dvc://`)
106
+
107
+ 统一语法(`selector.ts`):`scheme "://" path [ selector ]`,path 到首个 `:` 或 `?` 为止;selector ∈ `:raw` | `:N`/`:N-M`/`:N-M,N2-M2` | `:path/<dotpath>` | `?q=<q>`。`http`/`https` 在 `SELECTOR_EXEMPT` 中,整段都是 path。
108
+
109
+ | 用例 | 结果 | 判定 |
110
+ |---|---|---|
111
+ | `dsh://config:1-3` / `:2,4` / `:raw` | OK | 统一语法 ✓ |
112
+ | `dsh://config:path/agent-loop.maxParallelToolCalls` | OK → `10` | dot-path ✓ |
113
+ | `dsh://config?q=agent-loop` | OK → `{"maxParallelToolCalls":10}` | query ✓ |
114
+ | `dsh://config:0-2` / `:9-3` | ERR 1-based / empty range | 边界守卫 ✓ |
115
+ | `dsh://config:path/nope.missing` | **OK → 返回全文** | **静默回落(F8)** |
116
+ | `ctx://session:raw` / `:1-5` | OK | selectorAware 支持 raw/lines ✓ |
117
+ | `ctx://session/tool_calls[0]` | OK | **方括号写在 path 里** |
118
+ | `ctx://session/tool_calls[3]:1-2` | OK | 方括号 + 冒号 selector **可组合** |
119
+ | `ctx://session?q=user_prompts` | **ERR `CTX_BAD_SELECTOR` "unsupported selector kind \"query\""** | **不支持 query(F5)** |
120
+ | `ctx://session/tool_calls`(无括号) | ERR `no such element (collection "tool_calls" has 37 items, 0-based…)` | 集合需括号(F9) |
121
+ | `ctx://session/compactions`(无括号) | OK 列出 | 与上条不一致(F9) |
122
+ | `ctx://session/compactions[0]/original` | ERR no such episode(本会话 0 次 compaction) | `/original` = path 后缀 |
123
+ | `dvc://browser:1-3` / `dvc://:1-3` | OK | 非 selectorAware → 统一 selector 生效 ✓ |
124
+ | `dvc://ast_grep?q=lsp` | OK → 空(无命中行) | 非 JSON 走行过滤 ✓ |
125
+ | `agent://<id>/transcript:1-4` / `:raw` / `?q=sleep` | OK(child live 时) | 统一 selector ✓ |
126
+ | `agent://<id>/bogus` | ERR `unknown child agent "bogus" of "<id>"` | **L2 ≠ 自由 subpath,而是"后代 agent"(F10)** |
127
+
128
+ ### ctx 的语法实现(源码锚点)
129
+
130
+ `handlers/ctx.ts` 自述 `selectorAware: true`;`applyFace()` 只处理 `raw` 与 `lines`,其余一律 `CTX_BAD_SELECTOR`。path 解析为:
131
+
132
+ ```
133
+ // ── path parsing: session[/seg[[bracket]]…] ──
134
+ const sub = raw.slice('session'.length).replace(/^\/+/, '')
135
+ const segments = sub === '' ? [] : sub.split('/').filter(x => x !== '')
136
+ const parseSeg = (seg) => { const m = /^([a-z_]+)(?:\[(.+)\])?$/.exec(seg) … }
137
+ ```
138
+
139
+ ⇒ **`ctx://` 拥有"括号在 path + 冒号 selector 混用"的杂交语法**,与 `selector.ts` 的统一语法并列而非从属。
140
+
141
+ ---
142
+
143
+ ## 五、发现的偏差与缺陷
144
+
145
+ | # | severity | 结论 | 证据 |
146
+ |---|---|---|---|
147
+ | F1 | 高(已修) | `ctx://` 自 `074b6ae`(v0.1.8c) 起缺席**所有** `read` 枚举;且是唯一被系统提示词单列 bullet 的 scheme | `git log -S'skill/agent/dsh/dvc/ctx'` 零命中;实测 `read` 描述互文 |
148
+ | F2 | 中 | scheme 枚举点实为 **6 处**,非 user 以为的 2 处 → 已全部收敛到 `catalog.ts` | §二改动清单 |
149
+ | F3 | **高(未修)** | `dsh://docs` 在 `grep.ts`/`glob.ts`/`resolver.ts` 三处文档块声明为 **path-backed**,但**只有 `skill.ts` 实现了 `resolvePath`**,`dsh.ts` 没有 → grep/glob 实际走物化。后果:`glob("**/*.md", path="dsh://docs")` 返回 **0**(真实目录本应命中全部 md) | `grep -rn resolvePath handlers/` 仅 `skill.ts:159`;实测 `/dev/shm/…` |
150
+ | F4 | 中(未修) | `glob(pattern="dsh://docs")` 把 JSON 数组每行当 path 返回(`[`、` "agent-lifecycle.md",`)——"content-backed 的行即列表"规则对 JSON 资源语义错误 | 实测 paths |
151
+ | F5 | 中(未修) | `ctx://` 拒绝 4 种 selector 中的 2 种(`:path/`、`?q=`),违反"五 scheme 共享一套 selector 语法" | `CTX_BAD_SELECTOR`;`applyFace()` |
152
+ | F6 | 低(未修) | `dvc://<device>/<sub>` 静默丢弃 `/sub`(`deviceNameFromPath` 只取首段)——无告警,易误以为二级寻址生效 | `dvc.ts` + 实测 226B 相同 |
153
+ | F7 | 中(未修) | bare 枚举规则不一致:`ctx://`/`dvc://`/`agent://` 列 roster;`dsh://` 报错但自我说明;`skill://` 报错且**误导**(`skill "" is unknown`)。文档"Bare `read <scheme>://` lists that scheme's surface"对 2/5 不成立 | 实测 |
154
+ | F8 | 中(未修) | dot-path 未命中**静默回落全文**(`dsh://config:path/nope.missing` → 整个 config)——拼写错误无任何反馈 | `applyPath`:`node === undefined ? text` |
155
+ | F9 | 低(未修) | ctx 集合行为不一致:`compactions` 无括号可列;`tool_calls`/`user_prompts`/`agent_responses` 无括号报错(好在报错含条数) | 实测 |
156
+ | F10 | 低(信息) | `agent://<id>/<x>`:`transcript` 为保留字,其余按"后代 agent id"解析;且 `/transcript` 要求 session **live** | 报错文案 + live child 对照实测 |
157
+ | F11 | 低(环境) | 本 runtime `skill://` 目录为空(`.agents/skills` 未被 skill provider 挂载)→ 全矩阵唯一完全不可用 scheme,且 `skill://` 的 path-backed 快路径因此**实际不可达** | 实测 + 早前 `skill://` 解析失败 |
158
+ | F12 | 中(未修) | **dvc device 的相对路径按服务进程 cwd 解析,而非 agent session cwd**:同一 `{path:"dashr/…"}` 相对路径失败、绝对路径成功 | 实测对照 |
159
+ | F13 | 低(未修) | device 参数 schema **不可发现**:`dvc://<device>` 只回一行 summary + 泛化 usage;`ast_grep` 的 summary 恰含 `{patterns,path?}`,而 `browser` 完全不含 `action` 判别键(实测须猜 `{"action":"open","url":…}` 才通) | 实测:`{"code":"close"}` → `unknown action null`;`{"action":"open","url":"about:blank"}` → OK(随后已 `{"action":"close"}` 清理) |
160
+
161
+ ---
162
+
163
+ ## 六、验证与基线对照
164
+
165
+ ### 副本(Dev/Test 1)全量单测
166
+
167
+ ```
168
+ Test Files 1 failed | 38 passed (39)
169
+ Tests 1 failed | 482 passed (483)
170
+ ```
171
+
172
+ 唯一失败:`test/url-schemes/fs-backend.spec.ts > UrlAwareFileSystem backend (phase-2 spike) > builds the backend when the sandbox base resolves (dev workspace)`。
173
+
174
+ ### 基线对照(改动前,同一副本、同一子集)
175
+
176
+ ```
177
+ Test Files 1 failed | 18 passed (19)
178
+ Tests 1 failed | 273 passed (274)
179
+ ```
180
+
181
+ ⇒ 失败集**完全同一**(同一 test 名)⇒ **本次改动零回归**;新增的 6-scheme 护栏断言在 482 通过之列。
182
+
183
+ ### tsc
184
+
185
+ - 我改动的 5 个源文件 + 1 个 spec:**0 error**(`grep -cE 'catalog|general-section|selector|tools/read|tools/write'` = 0)。
186
+ - 副本残留 **1 个环境性 error**:`src/url-schemes/fs-backend.ts(67,31) TS2307 Cannot find module '@deepseek-ai/dsh-fs-sandbox'`。已证实:(a) 该文件 `git diff HEAD` 为空(未被本改动触碰);(b) 该模块在副本 `node_modules` 中确实不存在——AGENTS.md §二 的 devDeps 手术名单(14 个)本就不含 `dsh-fs-sandbox`;(c) 与上表既存失败同域。**非本次引入**。
187
+
188
+ ### canonical 侧说明
189
+
190
+ canonical `./dashr` 直接 `tsc` 有 9 处 error(`index.ts` 的 `tool/ptc-dispatch` 事件、`handlers/agent.ts` 的 `snapshotEvents`、`url-schemes/index.ts` 的 `SessionPersistenceSurface`、测试里的 `SessionSeq`/`isSeeded`)。这符合 AGENTS.md §二:canonical 保持 npm-range optional peers(发布语义),类型自洽只存在于做了 `workspace:*` 手术的副本。上述 error 全部**不涉及本次改动文件**。
191
+
192
+ ---
193
+
194
+ ## 七、未验证 / 遗留 / 待裁决
195
+
196
+ 1. **未构建、未重启 4999**:本次只落 **canonical 源 + 副本源**(6 文件已 `cp` 到位)。`lib/` 仍是改动前的构建,故 §三/§四 的矩阵描述的是**改前行为**(正是本次要刻画的对象);新的提示词/工具描述**尚未生效**。未执行 `tsdown && tsx scripts/build-client.ts` 与重启,理由:**重启 4999 会终止本 agent 会话**,且 `tsdown` 会先清空 `lib/`(含 `lib/client/`),中途失败会把 user 正在用的实例留在坏构建上——需 user 明确指令后再做。
197
+ 2. **F3/F4/F12 属行为缺陷,未修**:修 F3 需在 `dsh.ts` 实现 `resolvePath`(或修正三处文档块承认物化);修 F4 需为 JSON 资源定义"列表"语义。均超出"centralize"授权范围,等裁决。
198
+ 3. **F5/F6/F7/F8/F9 属语法一致性问题,未修**:是否统一(让 ctx 也吃 `?q=`、让 dvc 对多余段报错、让 skill:// bare 列 roster、让 dot-path 未命中报错)需 product 裁决——每条都会改变现有可观测行为。
199
+ 4. **prompt 预算 +847 chars**:若 user 认为常驻开销过高,可退化为"表格只在 `read` 描述内、系统段仅保留 grammar + 一行 scheme 名单"(总量不减,但可把表格从每轮系统提示挪到工具描述;或裁剪示例)。
200
+ 5. `skill://` 的目录为空是**部署面**问题(`.agents/skills` 未被 skill provider 挂载),与本次代码改动无关;是否挂载待定。
201
+
202
+ ---
203
+
204
+ ## 八、复现命令
205
+
206
+ ```bash
207
+ # 渲染单一源产物(对照 §二 的预算与 §三 的枚举)
208
+ cd /home/u1/workspaces/dashr/dashr
209
+ ./node_modules/.bin/tsx -e "
210
+ import { renderGeneralSectionText, renderExampleHint, SCHEME_NAMES } from './src/url-schemes/catalog.ts'
211
+ console.log(SCHEME_NAMES.join(',')); console.log(renderExampleHint())
212
+ console.log(renderGeneralSectionText())"
213
+
214
+ # 副本全量验证(AGENTS.md §二 的权威验证位)
215
+ cd /home/u1/workspaces/dashr/upstream/deepseek-harness/packages/better-dsh/better-dsh
216
+ ../../../node_modules/.bin/tsc --noEmit
217
+ ../../../node_modules/.bin/vitest --run
218
+
219
+ # 活体矩阵(在 4999 会话的 eval pad 内)
220
+ # await tool.read({"path":"ctx://session/tool_calls[0]"})
221
+ # await tool.write({"file_path":"dvc://ast_grep","content":'{"patterns":["x"],"path":"/abs/path"}'})
222
+ # await tool.glob({"pattern":"**/*.md","path":"dsh://docs"})
223
+ ```
224
+
225
+ ---
226
+
227
+ ## 附:基线对照的原始计数
228
+
229
+ | 运行 | 范围 | 结果 |
230
+ |---|---|---|
231
+ | 副本(改前) | `test/url-schemes test/url-schemes.spec.ts test/surface-devices` | 274 tests / **1 failed** |
232
+ | 副本(改后) | 同上 | 274 tests / 1 failed(同一 test) |
233
+ | 副本(改后) | **全量** | **483 tests / 1 failed** |
234
+ | canonical(改后) | 同上子集 | 14+ failed(`SessionSeq`/`isSeeded` 类型漂移所致,非本次引入) |