better-dsh 0.2.3-e → 0.2.3-g

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 (270) hide show
  1. package/docs/50_test-reports/2026-09-13-preact-ui-shell/345/256/236/346/265/213/346/212/245/345/221/212.md +1 -1
  2. package/docs/50_test-reports/2026-09-14-4999-skill/346/270/205/345/215/225/344/270/216lsp-gate/345/256/236/346/265/213/346/212/245/345/221/212.md +54 -0
  3. package/docs/specs/agent/spec.md +54 -0
  4. package/docs/specs/ast/spec.md +34 -0
  5. package/docs/specs/compaction-recall/spec.md +46 -0
  6. package/docs/specs/ctx/spec.md +107 -0
  7. package/docs/specs/dsh/spec.md +47 -0
  8. package/docs/specs/dvc/spec.md +87 -0
  9. package/docs/specs/escalation-guidance/spec.md +44 -0
  10. package/docs/specs/fs-scheme-resolution/spec.md +37 -0
  11. package/docs/specs/hash-edit/spec.md +41 -0
  12. package/docs/specs/http-read/spec.md +73 -0
  13. package/docs/specs/kernel-provisioning/spec.md +53 -0
  14. package/docs/specs/lsp/spec.md +121 -0
  15. package/docs/specs/mobile-layout/spec.md +108 -0
  16. package/docs/specs/model-failover/spec.md +20 -0
  17. package/docs/specs/preact-ui-shell/spec.md +22 -0
  18. package/docs/specs/repl-dispatch-resilience/spec.md +21 -0
  19. package/docs/specs/skill/spec.md +58 -0
  20. package/docs/specs/tool-surface/spec.md +222 -0
  21. package/docs/specs/url-schema/spec.md +148 -0
  22. package/docs/specs/web-trust-fence/spec.md +43 -0
  23. package/dsh-docs/AGENTS.md +75 -0
  24. package/dsh-docs/agent-lifecycle.md +84 -0
  25. package/dsh-docs/agent-lifecycle.zh.md +86 -0
  26. package/dsh-docs/api-gateway.md +164 -0
  27. package/dsh-docs/api-gateway.zh.md +164 -0
  28. package/dsh-docs/architecture.md +150 -0
  29. package/dsh-docs/architecture.zh.md +154 -0
  30. package/dsh-docs/capability-seams.md +543 -0
  31. package/dsh-docs/capability-seams.zh.md +545 -0
  32. package/dsh-docs/config-catalog.md +3473 -0
  33. package/dsh-docs/config-catalog.zh.md +3474 -0
  34. package/dsh-docs/cookbook/adding-a-package.md +117 -0
  35. package/dsh-docs/cookbook/adding-a-package.zh.md +119 -0
  36. package/dsh-docs/cookbook/adding-a-remote-api.md +197 -0
  37. package/dsh-docs/cookbook/adding-a-remote-api.zh.md +197 -0
  38. package/dsh-docs/cookbook/adding-a-settings-card.md +102 -0
  39. package/dsh-docs/cookbook/adding-a-settings-card.zh.md +102 -0
  40. package/dsh-docs/cookbook/adding-a-tool.md +101 -0
  41. package/dsh-docs/cookbook/adding-a-tool.zh.md +103 -0
  42. package/dsh-docs/cookbook/adding-a-vendored-package.md +59 -0
  43. package/dsh-docs/cookbook/adding-a-vendored-package.zh.md +59 -0
  44. package/dsh-docs/cookbook/adding-an-llm-adapter.md +43 -0
  45. package/dsh-docs/cookbook/adding-an-llm-adapter.zh.md +43 -0
  46. package/dsh-docs/cookbook/extension-cookbook.md +132 -0
  47. package/dsh-docs/cookbook/extension-cookbook.zh.md +136 -0
  48. package/dsh-docs/cookbook/maintaining-dsh-code-review.md +64 -0
  49. package/dsh-docs/cookbook/maintaining-dsh-code-review.zh.md +64 -0
  50. package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
  51. package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
  52. package/dsh-docs/cordis-api/context.md +364 -0
  53. package/dsh-docs/cordis-api/context.zh.md +366 -0
  54. package/dsh-docs/cordis-api/events.md +207 -0
  55. package/dsh-docs/cordis-api/events.zh.md +209 -0
  56. package/dsh-docs/cordis-api/fiber.md +375 -0
  57. package/dsh-docs/cordis-api/fiber.zh.md +377 -0
  58. package/dsh-docs/cordis-api/inherited.md +39 -0
  59. package/dsh-docs/cordis-api/registry.md +152 -0
  60. package/dsh-docs/cordis-api/registry.zh.md +154 -0
  61. package/dsh-docs/cordis-api/service.md +102 -0
  62. package/dsh-docs/cordis-api/service.zh.md +104 -0
  63. package/dsh-docs/cordis-primer.md +45 -0
  64. package/dsh-docs/cordis-primer.zh.md +51 -0
  65. package/dsh-docs/cordis-tutorial/01-first-plugin.md +95 -0
  66. package/dsh-docs/cordis-tutorial/01-first-plugin.zh.md +95 -0
  67. package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
  68. package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.zh.md +98 -0
  69. package/dsh-docs/cordis-tutorial/03-services.md +98 -0
  70. package/dsh-docs/cordis-tutorial/03-services.zh.md +98 -0
  71. package/dsh-docs/cordis-tutorial/04-events.md +144 -0
  72. package/dsh-docs/cordis-tutorial/04-events.zh.md +144 -0
  73. package/dsh-docs/cordis-tutorial/05-config.md +84 -0
  74. package/dsh-docs/cordis-tutorial/05-config.zh.md +84 -0
  75. package/dsh-docs/cordis-tutorial/06-composition-and-hmr.md +113 -0
  76. package/dsh-docs/cordis-tutorial/06-composition-and-hmr.zh.md +113 -0
  77. package/dsh-docs/cordis-tutorial/07-into-the-harness.md +108 -0
  78. package/dsh-docs/cordis-tutorial/07-into-the-harness.zh.md +108 -0
  79. package/dsh-docs/cordis-tutorial/index.md +60 -0
  80. package/dsh-docs/cordis-tutorial/index.zh.md +62 -0
  81. package/dsh-docs/deepseek-llm-api-wire-extensions.md +163 -0
  82. package/dsh-docs/deepseek-llm-api-wire-extensions.zh.md +163 -0
  83. package/dsh-docs/defensive-patterns.md +33 -0
  84. package/dsh-docs/defensive-patterns.zh.md +35 -0
  85. package/dsh-docs/development.md +167 -0
  86. package/dsh-docs/development.zh.md +173 -0
  87. package/dsh-docs/event-producer-consumer.md +86 -0
  88. package/dsh-docs/event-producer-consumer.zh.md +88 -0
  89. package/dsh-docs/glossary.md +45 -0
  90. package/dsh-docs/glossary.zh.md +45 -0
  91. package/dsh-docs/graph-atlas.md +22 -0
  92. package/dsh-docs/graph-atlas.zh.md +24 -0
  93. package/dsh-docs/i18n/README.md +60 -0
  94. package/dsh-docs/i18n/README.zh.md +62 -0
  95. package/dsh-docs/i18n/style-samples.md +87 -0
  96. package/dsh-docs/i18n/terminology.md +214 -0
  97. package/dsh-docs/i18n/translation-prompt.md +263 -0
  98. package/dsh-docs/i18n/translation-rules.md +69 -0
  99. package/dsh-docs/i18n/translation-rules.zh.md +69 -0
  100. package/dsh-docs/module-graph.md +1411 -0
  101. package/dsh-docs/module-graph.zh.md +1413 -0
  102. package/dsh-docs/persistence-catalog.md +1075 -0
  103. package/dsh-docs/persistence-catalog.zh.md +1077 -0
  104. package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.md +113 -0
  105. package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
  106. package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
  107. package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
  108. package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
  109. package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
  110. package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
  111. package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
  112. package/dsh-docs/postmortem/README.md +18 -0
  113. package/dsh-docs/postmortem/README.zh.md +18 -0
  114. package/dsh-docs/rescope.md +53 -0
  115. package/dsh-docs/rescope.zh.md +53 -0
  116. package/dsh-docs/subsystems/README.md +61 -0
  117. package/dsh-docs/subsystems/README.zh.md +61 -0
  118. package/dsh-docs/subsystems/agent-team.md +207 -0
  119. package/dsh-docs/subsystems/agent-team.zh.md +207 -0
  120. package/dsh-docs/subsystems/approval.md +170 -0
  121. package/dsh-docs/subsystems/approval.zh.md +170 -0
  122. package/dsh-docs/subsystems/attachment.md +351 -0
  123. package/dsh-docs/subsystems/attachment.zh.md +351 -0
  124. package/dsh-docs/subsystems/client-modules.md +168 -0
  125. package/dsh-docs/subsystems/client-modules.zh.md +168 -0
  126. package/dsh-docs/subsystems/code-runtime.md +195 -0
  127. package/dsh-docs/subsystems/code-runtime.zh.md +195 -0
  128. package/dsh-docs/subsystems/commands.md +219 -0
  129. package/dsh-docs/subsystems/commands.zh.md +219 -0
  130. package/dsh-docs/subsystems/compaction.md +238 -0
  131. package/dsh-docs/subsystems/compaction.zh.md +238 -0
  132. package/dsh-docs/subsystems/conversation.md +258 -0
  133. package/dsh-docs/subsystems/conversation.zh.md +258 -0
  134. package/dsh-docs/subsystems/core.md +1209 -0
  135. package/dsh-docs/subsystems/core.zh.md +1219 -0
  136. package/dsh-docs/subsystems/credentials.md +329 -0
  137. package/dsh-docs/subsystems/credentials.zh.md +329 -0
  138. package/dsh-docs/subsystems/extensions.md +382 -0
  139. package/dsh-docs/subsystems/extensions.zh.md +382 -0
  140. package/dsh-docs/subsystems/feedback.md +266 -0
  141. package/dsh-docs/subsystems/feedback.zh.md +266 -0
  142. package/dsh-docs/subsystems/filesystem.md +505 -0
  143. package/dsh-docs/subsystems/filesystem.zh.md +505 -0
  144. package/dsh-docs/subsystems/goal.md +277 -0
  145. package/dsh-docs/subsystems/goal.zh.md +277 -0
  146. package/dsh-docs/subsystems/invariants.md +88 -0
  147. package/dsh-docs/subsystems/invariants.zh.md +88 -0
  148. package/dsh-docs/subsystems/jobs.md +290 -0
  149. package/dsh-docs/subsystems/jobs.zh.md +290 -0
  150. package/dsh-docs/subsystems/llm-streaming.md +1080 -0
  151. package/dsh-docs/subsystems/llm-streaming.zh.md +1086 -0
  152. package/dsh-docs/subsystems/lsp.md +202 -0
  153. package/dsh-docs/subsystems/lsp.zh.md +202 -0
  154. package/dsh-docs/subsystems/permission-presets.md +131 -0
  155. package/dsh-docs/subsystems/permission-presets.zh.md +131 -0
  156. package/dsh-docs/subsystems/persistence.md +395 -0
  157. package/dsh-docs/subsystems/persistence.zh.md +395 -0
  158. package/dsh-docs/subsystems/plan.md +87 -0
  159. package/dsh-docs/subsystems/plan.zh.md +87 -0
  160. package/dsh-docs/subsystems/sandbox.md +220 -0
  161. package/dsh-docs/subsystems/sandbox.zh.md +220 -0
  162. package/dsh-docs/subsystems/schedule.md +192 -0
  163. package/dsh-docs/subsystems/schedule.zh.md +192 -0
  164. package/dsh-docs/subsystems/scope.md +59 -0
  165. package/dsh-docs/subsystems/scope.zh.md +59 -0
  166. package/dsh-docs/subsystems/session-projection.md +354 -0
  167. package/dsh-docs/subsystems/session-projection.zh.md +354 -0
  168. package/dsh-docs/subsystems/session-query.md +509 -0
  169. package/dsh-docs/subsystems/session-query.zh.md +509 -0
  170. package/dsh-docs/subsystems/session-reference.md +219 -0
  171. package/dsh-docs/subsystems/session-reference.zh.md +219 -0
  172. package/dsh-docs/subsystems/session-telemetry.md +194 -0
  173. package/dsh-docs/subsystems/session-telemetry.zh.md +194 -0
  174. package/dsh-docs/subsystems/session-title.md +204 -0
  175. package/dsh-docs/subsystems/session-title.zh.md +204 -0
  176. package/dsh-docs/subsystems/session.md +1155 -0
  177. package/dsh-docs/subsystems/session.zh.md +1159 -0
  178. package/dsh-docs/subsystems/settings.md +405 -0
  179. package/dsh-docs/subsystems/settings.zh.md +405 -0
  180. package/dsh-docs/subsystems/shell.md +303 -0
  181. package/dsh-docs/subsystems/shell.zh.md +303 -0
  182. package/dsh-docs/subsystems/skills.md +354 -0
  183. package/dsh-docs/subsystems/skills.zh.md +354 -0
  184. package/dsh-docs/subsystems/slots.md +175 -0
  185. package/dsh-docs/subsystems/slots.zh.md +175 -0
  186. package/dsh-docs/subsystems/spill.md +117 -0
  187. package/dsh-docs/subsystems/spill.zh.md +117 -0
  188. package/dsh-docs/subsystems/storage.md +260 -0
  189. package/dsh-docs/subsystems/storage.zh.md +260 -0
  190. package/dsh-docs/subsystems/subagent.md +766 -0
  191. package/dsh-docs/subsystems/subagent.zh.md +770 -0
  192. package/dsh-docs/subsystems/subprocess.md +324 -0
  193. package/dsh-docs/subsystems/subprocess.zh.md +324 -0
  194. package/dsh-docs/subsystems/system-prompt.md +220 -0
  195. package/dsh-docs/subsystems/system-prompt.zh.md +220 -0
  196. package/dsh-docs/subsystems/terminal.md +184 -0
  197. package/dsh-docs/subsystems/terminal.zh.md +184 -0
  198. package/dsh-docs/subsystems/todo.md +32 -0
  199. package/dsh-docs/subsystems/todo.zh.md +32 -0
  200. package/dsh-docs/subsystems/token-meter.md +105 -0
  201. package/dsh-docs/subsystems/token-meter.zh.md +105 -0
  202. package/dsh-docs/subsystems/tools.md +720 -0
  203. package/dsh-docs/subsystems/tools.zh.md +720 -0
  204. package/dsh-docs/subsystems/typert.md +343 -0
  205. package/dsh-docs/subsystems/typert.zh.md +343 -0
  206. package/dsh-docs/subsystems/user-questions.md +178 -0
  207. package/dsh-docs/subsystems/user-questions.zh.md +178 -0
  208. package/dsh-docs/subsystems/web-client.md +95 -0
  209. package/dsh-docs/subsystems/web-client.zh.md +95 -0
  210. package/dsh-docs/subsystems/web-server.md +154 -0
  211. package/dsh-docs/subsystems/web-server.zh.md +154 -0
  212. package/dsh-docs/subsystems/web.md +206 -0
  213. package/dsh-docs/subsystems/web.zh.md +206 -0
  214. package/dsh-docs/subsystems/webhook.md +70 -0
  215. package/dsh-docs/subsystems/webhook.zh.md +70 -0
  216. package/dsh-docs/subsystems/workflow.md +278 -0
  217. package/dsh-docs/subsystems/workflow.zh.md +278 -0
  218. package/dsh-docs/subsystems/workspace.md +321 -0
  219. package/dsh-docs/subsystems/workspace.zh.md +321 -0
  220. package/dsh-docs/testing.md +54 -0
  221. package/dsh-docs/testing.zh.md +54 -0
  222. package/dsh-docs/tool-catalog.md +2225 -0
  223. package/dsh-docs/tool-catalog.zh.md +2233 -0
  224. package/dsh-docs/tool-execution-pipeline.md +62 -0
  225. package/dsh-docs/tool-execution-pipeline.zh.md +64 -0
  226. package/dsh-docs/user/develop/basic/config.md +106 -0
  227. package/dsh-docs/user/develop/basic/config.zh.md +106 -0
  228. package/dsh-docs/user/develop/basic/index.md +144 -0
  229. package/dsh-docs/user/develop/basic/index.zh.md +144 -0
  230. package/dsh-docs/user/develop/basic/publish.md +183 -0
  231. package/dsh-docs/user/develop/basic/publish.zh.md +183 -0
  232. package/dsh-docs/user/develop/basic/tool.md +52 -0
  233. package/dsh-docs/user/develop/basic/tool.zh.md +52 -0
  234. package/dsh-docs/user/develop/framework/events.md +143 -0
  235. package/dsh-docs/user/develop/framework/events.zh.md +143 -0
  236. package/dsh-docs/user/develop/framework/index.md +137 -0
  237. package/dsh-docs/user/develop/framework/index.zh.md +137 -0
  238. package/dsh-docs/user/develop/framework/service.md +148 -0
  239. package/dsh-docs/user/develop/framework/service.zh.md +150 -0
  240. package/dsh-docs/user/develop/practice/dynamic-cordis.md +15 -0
  241. package/dsh-docs/user/develop/practice/dynamic-cordis.zh.md +15 -0
  242. package/dsh-docs/user/develop/practice/index.md +155 -0
  243. package/dsh-docs/user/develop/practice/index.zh.md +155 -0
  244. package/dsh-docs/user/develop/practice/llm-adapter.md +189 -0
  245. package/dsh-docs/user/develop/practice/llm-adapter.zh.md +189 -0
  246. package/dsh-docs/user/guide/github-review.md +102 -0
  247. package/dsh-docs/user/guide/github-review.zh.md +102 -0
  248. package/dsh-docs/user/guide/index.md +30 -0
  249. package/dsh-docs/user/guide/index.zh.md +30 -0
  250. package/dsh-docs/user/guide/mcp-memory.md +101 -0
  251. package/dsh-docs/user/guide/mcp-memory.zh.md +101 -0
  252. package/dsh-docs/user/guide/network-proxy.md +85 -0
  253. package/dsh-docs/user/guide/network-proxy.zh.md +85 -0
  254. package/dsh-docs/user/guide/providers.md +190 -0
  255. package/dsh-docs/user/guide/providers.zh.md +190 -0
  256. package/dsh-docs/user/guide/python-sdk.md +150 -0
  257. package/dsh-docs/user/guide/python-sdk.zh.md +150 -0
  258. package/dsh-docs/user/guide/schedule.md +21 -0
  259. package/dsh-docs/user/guide/schedule.zh.md +21 -0
  260. package/dsh-docs/user/index.md +11 -0
  261. package/dsh-docs/user/index.zh.md +11 -0
  262. package/dsh-docs/web-styling.md +29 -0
  263. package/dsh-docs/web-styling.zh.md +29 -0
  264. package/lib/client/index.js +268 -38
  265. package/lib/fs-aware/sandbox-plugin.js +1 -1
  266. package/lib/index.js +1112 -1261
  267. package/lib/lsp-server-registry-B8DNonhS.js +3 -0
  268. package/lib/lsp-server-registry-BexQagaK.js +943 -0
  269. package/lib/{wrap-DC8O3SYz.js → wrap-JFjcWwZf.js} +42 -16
  270. package/package.json +2 -1
@@ -34,7 +34,7 @@ AGENTS.md §二 harness 本地 patch 已记档(换 tag 需重放)。
34
34
  | shell 内 react-dom 痕迹 | **`version:"18.3.1", rendererPackageName:"react-dom"` banner 在 shell** | **无**(唯一 `react-dom` 字串 = 平台模块种子表键 `{react:Y3, "react-dom":Y3, ...}`——同一个 preact 实例登记两个平台词,设计终态) |
35
35
  | pageerror | slot 重复注册 ×1 | slot 重复注册 ×1 |
36
36
 
37
- **pageerror 定责(A/B)**:`settings.general.item` slot id `compaction-tuning` 重复注册在 **stock 上同样存在**(factory id `Ba` vs `f5` 仅 minified 差异)→ **存量问题**,属 `.dsh-test` profile 的 compaction-tuning 本地插件,与渲染引擎无关,挂账另修。`better-dsh/client.js` 内的 `react-dom` 匹配为 pnpm 依赖路径字符串(`@tanstack+react-virtual@…_react-dom@18.3.1…`),stock/preact 两面同在,非本体。
37
+ **pageerror 定责(A/B + 发布前验尸修正)**:`settings.general.item` slot id `compaction-tuning` 重复注册在 **stock 上同样存在**(factory id `Ba` vs `f5` 仅 minified 差异)→ 与渲染引擎无关。**根因(publish 前验尸确认)**:canonical 树里有一份**未提交的半成品 compaction-tuning 吸收工作**(`src/compaction/` + `installCompactionTuning` 接线,随 rsync 进入 4999 副本),与 `.dsh-test` profile 仍挂着的 local-plugin 版 compaction-tuning **同 id 注册两次**——两因叠加。该半成品已 `git stash`(带说明消息)移出 0.2.3-e 发布内容,续作时须先摘除 profile 的 local-plugin 行再合入。`better-dsh/client.js` 内的 `react-dom` 匹配为 pnpm 依赖路径字符串(`@tanstack+react-virtual@…_react-dom@18.3.1…`),stock/preact 两面同在,非本体。
38
38
 
39
39
  **user 未亲自引爆项声明**:深交互面(聊天流式渲染、shiki 高亮、markdown 管线)未逐项人工过——零 console.error 为强信号非穷尽证明(PoC 同款边界声明)。
40
40
 
@@ -0,0 +1,54 @@
1
+ # 4999 实测报告 — skill:// 空路径清单 + lsp gate/fan-out(2026-09-14)
2
+
3
+ ## 被测构建
4
+ - canonical:`dashr/dashr` @ `0da6a9f`(skill:// bare list)+ `84a98dc`(lsp parity stages 2/3/5)+ `3a94ce1`(stage 4)+ `7373d4e`(gate)
5
+ - 部署:rsync → monorepo `packages/better-dsh/better-dsh`(tsdown + build-client)→ 4999 实例(unit `dsh-4999-test`,DSH_HOME=`.dsh-test`)重启后验证。
6
+
7
+ ## 方法
8
+ 真实 agent 会话(headless Chrome CDP 驱动 GUI,复用 `.scratch/cdp-chat.mjs`),会话 `session-3dc1ae5b`(workspace `--home-u1-workspaces-dashr--`),prompt 指示执行指定 tool call 并原样回报输出;tool result 从 session v3 jsonl(zstd)落账本取证。
9
+
10
+ ## 结果
11
+
12
+ ### 1. 裸 `skill://` → 可用技能清单 ✅
13
+ `read skill://`(无名字)返回 cwd 范围技能目录(`N available skill(s):` + 逐条 `skill://<name> — <description> (use when: …)`),实际返回 11 条(book-to-skill … markitdown),与会话 skill catalog 一致。此前"returns nothing"的空白路径消除。
14
+
15
+ ### 2. `write dvc://lsp {"action":"status"}` ✅
16
+ 返回 `{"ok":true,"gate":"unasked"}` —— gate 动作走通,session id 由 write 分发器自动注入,agent 无需手填。
17
+
18
+ ### 3. `{"action":"diagnostics","file":"<abs .ts>","all":true}` fan-out ✅
19
+ ```json
20
+ { "ok": true, "fanout": true,
21
+ "servers": ["typescript-language-server","biome","eslint","denols","tailwindcss"],
22
+ "diagnostics": [],
23
+ "summary": "fan-out …/src/index.ts: 0 diagnostic(s) across 5 server(s) — …" }
24
+ ```
25
+ 5 个覆盖 .ts 的注册 server 全部被查询、合并、按 server 标注,无崩溃。
26
+
27
+ ### 4. 结构化错误路径 ✅(负例)
28
+ 相对路径 `dashr/dashr/src/index.ts`(相对 session cwd 不存在)→ 结构化 `file not found: <cwd>/…`,非崩溃非静默。
29
+
30
+ ## 结论
31
+ 本轮三项改动(skill:// 清单、lsp gate、fan-out 诊断)在 4999 真实运行时第一人称实测通过。单测侧:lsp-device 22 过、lsp-gate 5 过、skill.spec 17 过;tsc/构建绿。
32
+
33
+ ## 遗留
34
+ - 真实 language server 端到端诊断(安装 pyright/ts 后取非空诊断、didOpen/didSave 同步行号对齐)未覆盖——本机当时无 server 二进制,fan-out 返回 0 视为"无 server/干净"两可;待装齐后补测。
35
+ - gate on 后的 warm-start/didOpen 预热延迟附挂路径需真实 server 才能观测。
36
+
37
+ ## 追加实测(同日,commit 351d942)— dvc://lsp 读面
38
+ - `read dvc://lsp/status`:同一会话先 `write {"action":"on"}` 后 read → `{"ok":true,"gate":"on"}` —— session 经 read env 注入生效,读面返回**调用会话**的 gate。
39
+ - `read dvc://lsp/diagnostics?file=<abs>&all=1`:query 经 selector 还原为参数,fan-out 5 server 正常返回。
40
+ - 写面保留 status 动作(与注入式分发对称),但查询的规范形式是读面。
41
+
42
+ ## 追加(同日,commit 660fd4b)— 两个活体缺陷修复
43
+ 1. **dvc://browser 被会话注入毒死**:dispatcher 把 `session` 塞进 args,browser device 严格 validator 报 `unknown field(s) "session"`。修复:session 改走 `execute(args, {session})` 带外传输(lsp gate 同步改读 ctx,兼容旧 in-args)。进程内实证:`dispatchDvcWrite('dvc://browser', open example.com, 'sess-1')` 真实启动 Chrome 并返回 `{"ok":true,...,"title":"Example Domain"}`。
44
+ 2. **read 普通文件路径失败**:wrapper 参数只声明 `path`,被委托方(host 原生 read 要求 `file_path`)收不到参数报 missing required property;非 string 返回值又撞 wrapper 的 string 输出 schema。修复:参数声明双键别名;委托前按被委托方**声明的 schema** 适配键名(opaque schema 原样透传);非 string 结果 JSON 归一。三个委托形态(file_path 声明/path 声明/opaque)单测覆盖。
45
+
46
+ ## 追加(同日,commit f53c609)— glob/grep URL 缺陷修复
47
+ - `glob pattern="skill://grp/*"` 曾把带 glob 元字符的 URL 整体传给 ripgrep 当字面路径(rg exit 2)。修复:`splitUrlGlob` 在路径段首个元字符(`*?[`)处切分——URL 部分走 resolvePath,尾部 glob 作为 rooted pattern 应用在资源真实目录内。进程内对真实 skill 目录实证:`skill://grp/*` 与 `skill://grp/**/*.md` 均在目录内解析,不再报 No such file。
48
+ - `grep path="skill://…"` 匹配路径改写回 URL 形式(绝对路径前缀改写 + 原生相对路径前置),模型看到 `skill://grp/SKILL.md` 而非内部磁盘位置。
49
+ - url-schema spec 的 "glob with a URL in pattern … globs the resource's real disk directory natively" 场景现在对带元字符的 URL 同样成立。
50
+
51
+ ## 追加(同日,commit 874edf2)— dsh://docs 指向上游官方文档
52
+ - 官方 harness 文档语料(241 个 md,源自 deepseek-ai/deepseek-harness docs/,经 dsh-dev-skill 抓取)vendor 进包内 `dsh-docs/`,随 `files` 发布。
53
+ - 解析顺序:显式 docsDir → `pkgRoot/dsh-docs` → `pkgRoot/docs` → `pkgRoot/../docs`;docs-dir 逐级上溯在每层先探 `dsh-docs`。
54
+ - 4999 部署后实测:`dsh://docs` 索引返回 agent-lifecycle/api-gateway/architecture/… 官方语料清单。
@@ -0,0 +1,54 @@
1
+ # agent Specification
2
+
3
+ ## Purpose
4
+
5
+ Let the model address the agent roster, output artifacts, and full transcripts via `agent://` URLs — the single home for what upstream split across roster tools and a separate `history://` scheme.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement: Agent roster addressing
10
+ The system SHALL let bare `agent://` return the roster of every live session, oldest first, as a five-column table: `id`, `status`, `kind`, `parent`, `last activity`. `status` comes from the live agent registry (`idle`/`running`; `-` when the session has no live agent); `kind` is the session header's `origin` (default `main`); `parent` is the delegating parent session id (`-` at top level); `last activity` is the last session event's time in ISO 8601 (`-` when the session has no events).
11
+
12
+ #### Scenario: Listing all agents
13
+ - **WHEN** the model reads bare `agent://`
14
+ - **THEN** the system returns one row per live session with all five columns, e.g. `id status kind parent last activity`
15
+
16
+ #### Scenario: Session without a live agent
17
+ - **WHEN** a roster row's session has no entry in the live agent registry
18
+ - **THEN** its `status` column renders `-`
19
+
20
+ ### Requirement: Agent output addressing
21
+ The system SHALL let `agent://<id>` return that agent's output artifact: the rendered content of its last non-empty assistant message (matching `SubagentResult.output` semantics), or empty text when the agent produced no non-empty assistant output. Addressing is scoped to the caller's own family (the caller itself and its descendant children) — ids outside the family return `AGENT_UNKNOWN_ID`.
22
+
23
+ #### Scenario: Reading a completed agent's output
24
+ - **WHEN** the model reads `agent://<finished agent id>` from within its family
25
+ - **THEN** the system returns the rendered text of that agent's last non-empty assistant message
26
+
27
+ #### Scenario: Reading an unknown agent
28
+ - **WHEN** the model reads `agent://<unknown id>` (or an id outside the caller's family)
29
+ - **THEN** the system returns the structured `AGENT_UNKNOWN_ID` error
30
+
31
+ ### Requirement: Agent transcript addressing
32
+ The system SHALL let `agent://<id>/transcript` return the agent's full derived message history in order, each message headed by its role (`assistant`, `user`, `tool result`, `system`), with tool calls rendered as `[tool: name] arguments` and errors as `[tool error] …`. Addressing follows the same family scoping as output addressing.
33
+
34
+ #### Scenario: Reading an agent's session history
35
+ - **WHEN** the model reads `agent://<id>/transcript` for an agent in the caller's family
36
+ - **THEN** the system returns every message of that session in order, role-headed
37
+
38
+ ### Requirement: Nested output addressing
39
+ The system SHALL let `agent://<id>/<child>` return a direct child's output artifact, resolving the child only through the parent's enumerated children. Unknown children, or children not live in the session store, return `AGENT_UNKNOWN_ID`. Paths with more than two segments return `AGENT_BAD_PATH`.
40
+
41
+ #### Scenario: Reading a nested child output
42
+ - **WHEN** the model reads `agent://<parent id>/<child id>` and the child is a direct child of that parent
43
+ - **THEN** the system returns the child's output artifact
44
+
45
+ #### Scenario: Child not enumerable from the parent
46
+ - **WHEN** the model reads `agent://<id>/<non-child id>`
47
+ - **THEN** the system returns the structured `AGENT_UNKNOWN_ID` error
48
+
49
+ ### Requirement: history has no scheme of its own
50
+ The system SHALL provide transcript history only under `agent://<id>/transcript`. There is no `history://` handler and no special-case error for it: `history://` produces the generic `URL_UNREGISTERED_SCHEME` error like any other unregistered scheme.
51
+
52
+ #### Scenario: history scheme is unregistered
53
+ - **WHEN** the model reads `history://<id>`
54
+ - **THEN** the system returns the structured `URL_UNREGISTERED_SCHEME` error listing the registered schemes (history not among them)
@@ -0,0 +1,34 @@
1
+ # ast Specification
2
+
3
+ ## Purpose
4
+ `ast` 是**纯 tool 设施**:无进程、无状态、无协议——文本进 → tree-sitter 语法树 → 结构匹配/改写 → 结果出。以 `dvc://ast` 设备面挂载(`ast_grep` 结构搜索 + `ast_edit` 结构改写,natives 懒加载),由 agent 在工具循环里主动调用;不设任何运行时拦截点、不订阅文件事件、不参与文本同步。与 hash-edit/LSP 的分工:结构问题归 ast,语义问题(类型/跨文件引用/诊断)归 LSP,锚点编辑归 hash-edit。
5
+
6
+ ## Requirements
7
+
8
+ ### Requirement: Pure tool semantics
9
+ The ast device SHALL behave as a stateless function: every call carries its inputs (pattern, paths, content) and returns its result; no server process, no document mirror, no cross-call state SHALL exist.
10
+
11
+ #### Scenario: Stateless call
12
+ - **WHEN** the agent invokes `ast_grep` twice with identical inputs
13
+ - **THEN** both calls parse independently and return identical results, with no cached tree or warm process in between
14
+
15
+ ### Requirement: Lazy natives, zero startup cost
16
+ Parser natives SHALL load lazily on first use (natives-loader); host start and agent session start SHALL NOT spawn anything or load grammar binaries.
17
+
18
+ #### Scenario: Cold session
19
+ - **WHEN** a session ends without ever invoking the ast device
20
+ - **THEN** no native library was loaded and no subprocess was created for it
21
+
22
+ ### Requirement: dvc scheme surface only
23
+ The ast capability SHALL expose exclusively through the `dvc://ast` write/read contract (bare `dvc://` roster lists it; `dvc://ast` read returns its doc; `dvc://ast` write dispatches). No dedicated tool name, no scheme branches in read/grep/glob, and no hook into read/write/edit flows SHALL be added.
24
+
25
+ #### Scenario: Surface inventory
26
+ - **WHEN** the model-facing tool surface is enumerated
27
+ - **THEN** ast appears only as the `dvc://ast` device — its invocation is an ordinary `write` the agent decides to make
28
+
29
+ ### Requirement: Structural semantics boundary
30
+ The ast device SHALL provide structural matching/rewriting only. It makes no claim about types, cross-file references, or diagnostics; a deployment wanting semantics SHALL use the lsp capability instead.
31
+
32
+ #### Scenario: No semantic answers
33
+ - **WHEN** the agent asks the ast device for type information or project diagnostics
34
+ - **THEN** no such capability exists on the device surface (the call is not offered)
@@ -0,0 +1,46 @@
1
+ # compaction-recall Specification
2
+
3
+ ## Purpose
4
+ TBD - created by archiving change 2026-09-11-url-schemes-recallable-context. Update Purpose after archive.
5
+
6
+ ## Requirements
7
+
8
+ ### Requirement: Compaction manifest with nested chain
9
+ The system SHALL expose the compaction manifest of the live session, one entry per successful compaction episode, each carrying: `label` (the `compaction/summary` event seq), `checkpoint_seq` (summary seq + 1), `compactionId`, timestamp, `shadowedRange`, `shadowedItems`, `shadowedTokenCount`, `replaces_checkpoint` (the previous episode's checkpoint seq when absorbed, else null), and an 8-section summary preview (≤100 chars per section).
10
+
11
+ #### Scenario: Manifest reflects nested compaction
12
+ - **WHEN** a session has two successful compactions where the second absorbed the first's checkpoint
13
+ - **THEN** the manifest lists both episodes and the second entry carries `replaces_checkpoint` equal to the first episode's checkpoint seq
14
+
15
+ ### Requirement: Label resolution precedes ordinal
16
+ For any bracket-addressed collection, the system SHALL match the label (the element's immutable seq coordinate) exactly first, and SHALL fall back to the 0-based ordinal only when no label matches. Compaction episodes SHALL use the `compaction/summary` event seq as label; failed compactions SHALL NOT appear in any collection because they never emit a summary event.
17
+
18
+ #### Scenario: Failed compaction is not addressable
19
+ - **WHEN** a compaction attempt fails (summary not smaller than shadowed content) and the model reads the compactions manifest
20
+ - **THEN** the failed episode is absent from the manifest and has no addressable label
21
+
22
+ ### Requirement: Canonical and prepared faces for compaction episodes
23
+ For `ctx://session/compactions[<label>]`, the bare URL SHALL return the episode's structured summary (all 8 sections verbatim) as the prepared default face, and `:raw` SHALL return the episode's canonical content — the original shadowed span derived from `shadowedSeqs` (role + content per item). Line windows SHALL always apply to the canonical content, and the composite `:raw:<lines>` form SHALL be valid and equal `:<lines>`. The former `/original` sub-path SHALL be removed — it was identical to `:raw` and is superseded by composing the `:raw` / `:N-M` selectors directly on the episode; a path using it SHALL be rejected with the structured `CTX_BAD_PATH` error echoing the URL. When an unwindowed `:raw` read of the canonical face exceeds 65536 chars, the system SHALL append an actionable note naming `:N-M` line-window paging; windowed reads (`:N-M`, `:raw:N-M`) and the bare prepared face SHALL NOT carry the note.
24
+
25
+ #### Scenario: Recall of the original span behind a summary
26
+ - **WHEN** the model reads `ctx://session/compactions[221217]:raw` after the episode shadowed 559 items
27
+ - **THEN** the system returns the original message-level content of all shadowed items, including tool results and assistant messages
28
+
29
+ #### Scenario: Chained recall to pre-previous originals
30
+ - **WHEN** the model reads `ctx://session/compactions[109245]:raw` (the first episode in a nested chain)
31
+ - **THEN** the system returns the first span's original content even though later compactions shadowed its checkpoint
32
+
33
+ #### Scenario: Unwindowed raw recall of an oversized span pages explicitly
34
+ - **WHEN** the model reads `ctx://session/compactions[221217]:raw` and the span exceeds 65536 chars
35
+ - **THEN** the system returns the full span followed by a note naming `:N-M` line-window paging, while `:raw:N-M` / `:N-M` return the windowed lines without the note
36
+
37
+ #### Scenario: Removed /original sub-path
38
+ - **WHEN** the model reads `ctx://session/compactions[221217]/original`
39
+ - **THEN** the system rejects the path with `CTX_BAD_PATH`, echoing the URL
40
+
41
+ ### Requirement: Recall is read-only and lazily materialized
42
+ The system SHALL serve all recall resources strictly read-only, SHALL build the episode index lazily from the session log (zstd-decompressed, single-pass seq index) with invalidation on session-file change, and SHALL NOT impose truncation on canonical content beyond the tool layer's own limits, surfacing an actionable truncation note instead.
43
+
44
+ #### Scenario: Oversized original request
45
+ - **WHEN** the model reads an original span exceeding the tool-layer response cap
46
+ - **THEN** the system returns the truncated head (or the requested line window) with a note naming the `:raw`/line-window addressing for the remainder
@@ -0,0 +1,107 @@
1
+ # ctx Specification
2
+
3
+ ## Purpose
4
+
5
+ Let the model read a curated, read-only snapshot of its calling environment via `ctx://` URLs — small, static, agent-derived facts (who am I, what model, what cwd) addressable like any other resource. This replaces the v0.1.8c design that mapped `ctx://` onto persistent-kernel variables; see design.md D4 for why that semantics was wrong (the kernel namespace is the model's own REPL scratchpad, not its environment).
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement: Curated snapshot keys
10
+ The system SHALL resolve `ctx://session` as the recallable-context statistics snapshot: the prepared default face SHALL carry the session header (absorbing the former identity fields `id`/`status`/`origin`/`delegationDepth`), storage facts, totals using native DSH field names, per-compaction segments plus a `live` tail segment, the inline compactions manifest (label, checkpoint_seq, compactionId, shadowedRange, shadowedItems, shadowedTokenCount, `replaces_checkpoint`, and an 8-section × ≤100-char summary preview per episode), and a `system_prompt` info card. The canonical face (see `:raw`) SHALL be the full session transcript. The first-level keys `model` and `cwd` SHALL be removed — their information SHALL appear only as info-card fields inside the snapshot. Any other first-level key SHALL return the structured `CTX_UNKNOWN_KEY` error listing the known keys and sub-paths.
11
+
12
+ #### Scenario: Reading session identity
13
+ - **WHEN** the model reads `ctx://session` from a delegated subagent session
14
+ - **THEN** the snapshot header carries the agent id, status, origin, and delegation depth
15
+
16
+ #### Scenario: Reading the model configuration
17
+ - **WHEN** the model reads `ctx://session`
18
+ - **THEN** the snapshot info card carries the provider, model, and maxTokens of the calling agent's request options
19
+
20
+ #### Scenario: Reading the working directory
21
+ - **WHEN** the model reads `ctx://session`
22
+ - **THEN** the session's creation working directory appears as an info-card field, not as a separate key
23
+
24
+ #### Scenario: Unknown key
25
+ - **WHEN** the model reads `ctx://<other key>`
26
+ - **THEN** the system returns the structured `CTX_UNKNOWN_KEY` error naming the known keys and sub-paths
27
+
28
+ ### Requirement: Bare listing
29
+ The system SHALL let bare `ctx://` return the roster of available resources and usage entry points (one per line), including the `session` root, the sub-path grammar pointer (naming the composite `:raw:N-M` form), and the `transcript`/`compactions`/`user_prompts`/`tool_calls`/`agent_responses`/`thinking`/`system`/`injections` sub-paths; the static portion SHALL not require a live agent, and the roster SHALL stay within its line budget (its previous size plus the three collection entries).
30
+
31
+ #### Scenario: Listing keys
32
+ - **WHEN** the model reads bare `ctx://`
33
+ - **THEN** the system returns the roster naming `session`, its sub-paths (including `thinking` and `system`), and the addressing-grammar pointer
34
+
35
+ ### Requirement: Snapshot requires a live agent
36
+ The system SHALL read every snapshot value from the calling agent supplied in the resolver env; an env with no live agent returns the structured `CTX_NO_AGENT` error on any value read.
37
+
38
+ #### Scenario: No agent in the context
39
+ - **WHEN** a ctx:// value read runs with no live agent in the resolver env
40
+ - **THEN** the system returns the structured `CTX_NO_AGENT` error
41
+
42
+ ### Requirement: ctx is strictly read-only
43
+ The system SHALL reject every write to `ctx://` with the structured `URL_READ_ONLY` error explaining the scheme is a curated read-only snapshot. There is no kernel-variable write channel and no variable mutation of any kind.
44
+
45
+ #### Scenario: Writing to a snapshot key
46
+ - **WHEN** the model writes to `ctx://<any key>`
47
+ - **THEN** the system returns the structured `URL_READ_ONLY` error and changes nothing
48
+
49
+ ### Requirement: Session sub-path grammar
50
+ The system SHALL resolve `ctx://session/…` sub-paths: `transcript` (full transcript), `compactions` (manifest), `compactions[<label|ordinal>]` (the episode summary, 8 sections verbatim), `user_prompts[<n|seq>]`, `tool_calls[<n|seq>]`, `agent_responses[<n|seq>]`, `thinking[<n|seq>]` (reasoning blocks), and `system[<n|seq>]` (system messages). The former `compactions[<label|n>]/original` sub-path SHALL be removed — it was identical to `:raw` and is superseded by composing the `:raw` / `:N-M` selectors directly on the episode; a path using it SHALL be rejected with the structured `CTX_BAD_PATH` error echoing the URL. Bracket resolution SHALL match the label (the element's immutable seq coordinate) exactly first, and SHALL fall back to the 0-based ordinal on miss. The system SHALL support `:raw` and line windows (`:N`, `:N-M`, `:N+K`, `:N-`, comma-separated ranges) on every resolved resource, and the composite `:raw:<lines>` form SHALL be valid everywhere and SHALL equal `:<lines>` (the `:raw` prefix is redundant in a line-window context but MUST parse).
51
+
52
+ #### Scenario: Drilling into a compaction episode by label
53
+ - **WHEN** the model reads `ctx://session/compactions[221217]`
54
+ - **THEN** the system returns that episode's structured summary (all 8 sections verbatim)
55
+
56
+ #### Scenario: Ordinal fallback
57
+ - **WHEN** the model reads `ctx://session/compactions[0]` and no episode carries the label `0`
58
+ - **THEN** the system returns the first-recorded episode (0-based)
59
+
60
+ #### Scenario: Composite raw selector
61
+ - **WHEN** the model reads `ctx://session/compactions[221217]:raw:500-560`
62
+ - **THEN** the system returns the same lines as `ctx://session/compactions[221217]:500-560`
63
+
64
+ #### Scenario: Removed /original sub-path
65
+ - **WHEN** the model reads `ctx://session/compactions[221217]/original`
66
+ - **THEN** the system returns the structured `CTX_BAD_PATH` error echoing the URL and naming the `:raw` / `:N-M` selectors as the replacement
67
+
68
+ ### Requirement: Canonical and prepared content faces
69
+ The system SHALL treat every resource as having one canonical content: `:raw` SHALL return the canonical full content, line windows SHALL always apply to the canonical content, and the bare URL SHALL return the prepared default face when one is prepared (session → statistics snapshot; compaction episodes → 8-section summary; `thinking` → per-block index list; `system` → per-message index list) or the canonical content when none is. `:raw:<lines>` SHALL equal `:<lines>` — the composite form is accepted on every resource.
70
+
71
+ #### Scenario: Line windows ignore the prepared face
72
+ - **WHEN** the model reads `ctx://session/compactions[221217]:500-560`
73
+ - **THEN** the system returns lines 500–560 of the episode's original shadowed span, not of the summary
74
+
75
+ ### Requirement: Thinking, system, and injections collections
76
+ The system SHALL expose three index-faced collections under `ctx://session/`, all following the canonical/prepared face model (bare URL = prepared index; `:raw` / `:N-M` = canonical full text):
77
+
78
+ - `thinking` — the reasoning blocks of `assistant/message` events (`message.content` blocks of `type: "reasoning"`, in event order; a message may carry several). The bare URL SHALL return an index list, one line per block: 0-based ordinal, event seq, turn/step, and a ≤100-char single-line preview. `[<n|seq>]` SHALL return that block's full text (label = the block's event seq, exact match first, 0-based ordinal fallback). `:raw` SHALL return all blocks' full text joined in event order with a blank line between blocks, and line windows SHALL index that canonical text.
79
+ - `system` — the session's `system/message` events (text from `data.text` when present, else the message content blocks; source kind/plugin from `data.source` when present). The bare URL SHALL return an index list, one line per message: event seq, source kind/plugin, and a ≤100-char single-line preview. `[<n|seq>]` SHALL return that message's full text. `:raw` SHALL return all messages' full text joined with a blank line between messages, and line windows SHALL index that canonical text.
80
+ - `injections` — the session's injected `user/message` events, i.e. every `user/message` whose `source.kind` is not `user` (agent instructions, runtime-context snapshots, …; the exact complement of the `user_prompts` collection). The bare URL SHALL return an index list, one line per message: event seq, source kind, and a ≤100-char single-line preview. `[<n|seq>]` SHALL return that message's full text rendered as `[<zero-padded seq>] INJECTED <kind>` followed by the content. `:raw` SHALL return all messages' full text joined with a blank line between messages, and line windows SHALL index that canonical text.
81
+
82
+ #### Scenario: Index then drill into a reasoning block
83
+ - **WHEN** the model reads `ctx://session/thinking` and then `ctx://session/thinking[2]`
84
+ - **THEN** the index lists one line per reasoning block (ordinal, seq, turn/step, preview) and the drilled read returns the third block's full text
85
+
86
+ #### Scenario: Canonical window over the joined blocks
87
+ - **WHEN** the model reads `ctx://session/thinking:10-20`
88
+ - **THEN** the system returns lines 10–20 of the reasoning blocks' full text joined with blank lines, ignoring the index face
89
+
90
+ #### Scenario: System message by seq label
91
+ - **WHEN** the model reads `ctx://session/system[7]` where event seq 7 is a system message
92
+ - **THEN** the system returns that message's full text
93
+
94
+ #### Scenario: Injections complement user_prompts
95
+ - **WHEN** the model reads `ctx://session/injections` in a session whose `user/message` events carry both `source.kind: "user"` and non-`user` sources (agent instructions, runtime snapshots)
96
+ - **THEN** the index lists only the non-`user` messages (seq, source kind, preview), `ctx://session/injections[<n|seq>]` returns one message's full text, and `ctx://session/user_prompts` continues to list only the real prompts
97
+
98
+ #### Scenario: Injection message by ordinal or seq
99
+ - **WHEN** the model reads `ctx://session/injections[0]` or `ctx://session/injections[<seq>]`
100
+ - **THEN** the system returns that injected message's full text with its `INJECTED <kind>` header line
101
+
102
+ ### Requirement: Unknown key echoes known keys
103
+ The `CTX_UNKNOWN_KEY` error SHALL list the currently known first-level keys and the sub-path pointer, so the model can self-correct without leaving the read tool.
104
+
105
+ #### Scenario: Unknown key with roster echo
106
+ - **WHEN** the model reads `ctx://bogus`
107
+ - **THEN** the system returns `CTX_UNKNOWN_KEY` naming `session` and the sub-path roster
@@ -0,0 +1,47 @@
1
+ # dsh Specification
2
+
3
+ ## Purpose
4
+
5
+ Let the model read the harness's own documentation and its effective configuration via `dsh://` URLs — runtime self-description, shipped with the package so it resolves in source, built, and installed layouts alike.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement: Documentation addressing
10
+ The system SHALL let `dsh://docs` return the sorted recursive listing of readable harness docs as JSON, and `dsh://docs/<doc>` return that document's content. A missing docs tree returns `URL_DOCS_UNAVAILABLE`; a missing document, a path escaping the docs directory, or a non-file target returns `URL_DOC_NOT_FOUND`. Any other root resource returns `URL_UNKNOWN_RESOURCE`.
11
+
12
+ #### Scenario: Browsing the doc listing
13
+ - **WHEN** the model reads `dsh://docs`
14
+ - **THEN** the system returns the JSON array of doc paths relative to the docs root
15
+
16
+ #### Scenario: Reading a specific document
17
+ - **WHEN** the model reads `dsh://docs/<doc>`
18
+ - **THEN** the system returns that document's content
19
+
20
+ #### Scenario: Path traversal is rejected
21
+ - **WHEN** the model reads `dsh://docs/../secrets`
22
+ - **THEN** the system returns the structured `URL_DOC_NOT_FOUND` error (path escapes the docs directory) and reads nothing
23
+
24
+ ### Requirement: Docs tree resolves in every layout
25
+ The system SHALL locate the docs tree by a nearest-first walk-up from the module's own location (`docs-dir.ts`), and the package SHALL ship the docs (`prebuild` copies the repo `docs/` into the package; the `files` array includes it) so source-tree, bundled-lib, and installed-`node_modules` layouts all resolve the package's own docs first.
26
+
27
+ #### Scenario: Installed package resolves its own docs
28
+ - **WHEN** the plugin runs from `node_modules/<dashr>/lib/index.js`
29
+ - **THEN** `dsh://docs` serves the docs shipped inside that package, not an ancestor directory's
30
+
31
+ ### Requirement: Effective config addressing
32
+ The system SHALL let `dsh://config` return the current resolved settings as JSON keyed by namespace, and `dsh://config/<ns>` return one namespace. A missing settings service returns `URL_SETTINGS_UNAVAILABLE`; an unknown namespace returns `URL_UNKNOWN_SETTINGS_NAMESPACE`.
33
+
34
+ #### Scenario: Reading the effective config
35
+ - **WHEN** the model reads `dsh://config`
36
+ - **THEN** the system returns the resolved (not documented-default) configuration, namespace by namespace
37
+
38
+ #### Scenario: Reading one namespace
39
+ - **WHEN** the model reads `dsh://config/<known namespace>`
40
+ - **THEN** the system returns that namespace's resolved value as JSON
41
+
42
+ ### Requirement: Config never leaks secrets
43
+ The system SHALL strip secrets from every config response: schema-declared `role('secret')` redaction plus a defensive key-name denylist matched against normalized key names (credential/env/API-key material), applied recursively.
44
+
45
+ #### Scenario: Config hides keys
46
+ - **WHEN** the model reads `dsh://config` or `dsh://config/<ns>` and a resolved field is an API key or other secret-named field
47
+ - **THEN** the returned JSON does not contain the secret value
@@ -0,0 +1,87 @@
1
+ # dvc Specification
2
+
3
+ ## Purpose
4
+
5
+ Reserve DASHR's device I/O surface under `dvc://` (renamed from the earlier `xd://` placeholder — no `xd` name remains anywhere): a read = device list/document view, a write = device dispatch entry. No device provider is mounted this wave, so both views are placeholders that fix the URL shapes and the structured write error for whatever device layer lands later.
6
+
7
+ ## Requirements
8
+
9
+ ### Requirement: Device roster placeholder
10
+ `dvc://` SHALL return the mounted-device roster listing every registered device name. With no device modules loaded the roster is the placeholder text `no devices mounted`; once devices are registered it lists their names.
11
+
12
+ #### Scenario: Listing mounted devices
13
+ - **WHEN** the model reads bare `dvc://`
14
+ - **THEN** the system returns the roster of registered device names (or `no devices mounted` when none)
15
+
16
+ ### Requirement: Unknown device placeholder
17
+ The system SHALL let `dvc://<device>` return placeholder text `unknown device: <name>` — no device provider exists to answer with a real document.
18
+
19
+ #### Scenario: Reading an unmounted device
20
+ - **WHEN** the model reads `dvc://<any device name>`
21
+ - **THEN** the system returns `unknown device: <name>`
22
+
23
+ ### Requirement: Write dispatch is a structured no-op
24
+ Every write to `dvc://<device>` SHALL dispatch to the registered device's execute with the JSON-args payload; with no devices mounted the structured `DVC_NO_DEVICE` error stands, and an unknown device name remains a structured error.
25
+
26
+ #### Scenario: Writing with no devices
27
+ - **WHEN** the model writes to `dvc://<device>` and no device module is registered
28
+ - **THEN** the system returns the structured `DVC_NO_DEVICE` error and dispatches nothing
29
+
30
+ ### Requirement: Device dispatch contract
31
+ The system SHALL implement the device write contract: `write dvc://<device>` with a JSON-args content executes the device and returns its result; a non-JSON content or a device-reported failure returns a structured error carrying the device name.
32
+
33
+ #### Scenario: Executing a registered device
34
+ - **WHEN** the model writes `dvc://ast_edit` with valid JSON args
35
+ - **THEN** the device executes and its result returns to the caller
36
+
37
+ ### Requirement: ast devices
38
+ The system SHALL provide `ast_edit` (staged structured codemod) and `ast_grep` (structured search) devices, vendored from the omp harness (MIT), backed by the published `@oh-my-pi/pi-natives` binding.
39
+
40
+ #### Scenario: ast_grep search over a workspace file
41
+ - **WHEN** the model writes `dvc://ast_grep` with a pattern and path
42
+ - **THEN** structured matches return from the AST search
43
+
44
+ ### Requirement: browser device
45
+ The system SHALL provide a `browser` device (open/close/run over real browser tabs) vendored from the omp harness, using puppeteer-core against the system Chrome; when no browser can launch, the device returns a structured error.
46
+
47
+ #### Scenario: Opening a page headlessly
48
+ - **WHEN** the model writes `dvc://browser` with an open action and URL
49
+ - **THEN** a headless tab opens and the action result returns
50
+
51
+ ### Requirement: lsp device
52
+ The system SHALL provide an `lsp` device (definition/references/diagnostics/actions) vendored from the omp harness; languages whose server binary is absent degrade gracefully per language.
53
+
54
+ #### Scenario: Diagnostics for an installed language server
55
+ - **WHEN** the model writes `dvc://lsp` requesting diagnostics for a file whose language server is installed
56
+ - **THEN** the device returns the diagnostics
57
+
58
+ #### Scenario: Missing language server degrades
59
+ - **WHEN** the requested language's server binary is not installed
60
+ - **THEN** the device reports the missing server without crashing the session
61
+
62
+ ### Requirement: lsp wired into write
63
+ The system SHALL close the write-feedback loop: after a `write` lands a file whose language has an available language server, the tool result SHALL include a diagnostics summary for that file (error/warning counts plus the first message detail when non-zero), and the content SHALL be formatted before the single native write when the language server provides formatting capability. The diagnostics pipeline SHALL be honest about freshness: the feedback path syncs the exact written content, then signals the standard save notification (`textDocument/didSave`) so save-triggered checkers (e.g. rust-analyzer's flycheck) re-run, and waits for the refreshed diagnostics under a bounded timeout. When the wait times out, save-triggered compiler-source diagnostics (which are provably stale at that point) SHALL be dropped while immediately-computed diagnostics are kept — under-reporting beats mis-reporting. As a final guard, any diagnostic whose line lies beyond the just-written content's line count SHALL be dropped: it cannot refer to what was written. Languages with no server available (absent binary or unsupported extension) SHALL behave exactly as before — the hook adds nothing and fails silently, and the native write receives the caller's arguments object unchanged when formatting changes nothing.
64
+
65
+ #### Scenario: Write surfaces the damage it just caused
66
+ - **WHEN** a write lands content that introduces a type error in a file with a language server installed and its check-on-save pipeline completes within the timeout
67
+ - **THEN** the write result carries a diagnostics summary describing the EXACT content just written (never a stale earlier version), so the model learns of the breakage without a separate diagnostics call
68
+
69
+ #### Scenario: A fixed error stops being reported
70
+ - **WHEN** a write replaces content that previously had a type error with correct content
71
+ - **THEN** the write result no longer reports the old error — the save-triggered checker re-ran on the new content, and any compiler-source diagnostic still describing the old content is either refreshed or dropped
72
+
73
+ #### Scenario: Slow checks degrade honestly
74
+ - **WHEN** the save-triggered check does not complete within the bounded timeout
75
+ - **THEN** compiler-source diagnostics are dropped from the summary (they are provably stale), immediately-computed diagnostics remain, and the result never reports an error that refers to content other than what was just written
76
+
77
+ #### Scenario: Out-of-range spans are dropped
78
+ - **WHEN** a published diagnostic references a line beyond the just-written content's line count
79
+ - **THEN** that diagnostic is excluded from the summary and the counts reflect only the retained set
80
+
81
+ #### Scenario: Format-before-write
82
+ - **WHEN** a write targets a file whose server provides formatting
83
+ - **THEN** the native write receives and stores the formatted content (one write, one audit), and the before/after pair stays truthful
84
+
85
+ #### Scenario: Serverless language unchanged
86
+ - **WHEN** a write targets a file whose language has no server available
87
+ - **THEN** the result is byte-identical to the pre-change behavior (no diagnostics block, no formatting, no error)
@@ -0,0 +1,44 @@
1
+ # escalation-guidance Specification
2
+
3
+ ## Purpose
4
+ 在 workspace-write 沙箱模式下,向模型的运行时上下文注入 per-call 升级能力的精简披露:受限操作可被拒/已拒后按调用以 `sandbox_permissions` + 一行 `justification` 升级(runtime 提示 user 审批,审批/拒绝均为 per-call,无会话级预算措辞)。该注入只披露能力、不劝导行为,且不依赖 upstream 源码修改。
5
+
6
+ ## Requirements
7
+
8
+ ### Requirement: Escalation guidance injected under workspace-write only
9
+
10
+ The system SHALL inject an escalation-guidance context entry into the model-facing system prompt when the session's effective sandbox mode is `workspace-write`, and SHALL NOT inject it when the effective mode is `read-only` or `danger-full-access`. The guidance SHALL state the per-call escalation semantics in the approved wording: a sandbox-deniable or sandbox-denied call MAY be escalated with `sandbox_permissions` and a one-line `justification`; the runtime SHALL prompt the user for approval; escalation and its approval/denial SHALL be per-call. The guidance text SHALL NOT contain quota wording (`retried once`, `once`, allowed-once) or any claim of a session-level escalation budget.
11
+
12
+ #### Scenario: Workspace-write session sees the guidance
13
+
14
+ - **WHEN** a DASHR agent session runs with effective sandbox mode `workspace-write`
15
+ - **THEN** the runtime-context snapshot contains the escalation-guidance entry stating the deniable/denied per-call escalation path (`sandbox_permissions` + one-line `justification`, user approval prompted by the runtime)
16
+
17
+ #### Scenario: Guidance contains no quota wording
18
+
19
+ - **WHEN** the escalation-guidance entry renders under `workspace-write`
20
+ - **THEN** its text contains none of `retried once` / allowed-once / per-session budget claims, and states that escalation and its approval/denial are per-call
21
+
22
+ #### Scenario: Read-only session skips the guidance
23
+
24
+ - **WHEN** a DASHR agent session runs with effective sandbox mode `read-only`
25
+ - **THEN** the runtime-context snapshot contains no escalation-guidance entry from DASHR (the upstream read-only policy sentence already teaches the escalation guidance)
26
+
27
+ #### Scenario: Danger-full-access session skips the guidance
28
+
29
+ - **WHEN** a DASHR agent session runs with effective sandbox mode `danger-full-access`
30
+ - **THEN** the runtime-context snapshot contains no escalation-guidance entry (there is no restricted operation to escalate)
31
+
32
+ ### Requirement: Guidance is minimal disclosure, not behavioral coaching
33
+ The system SHALL keep the injected guidance text limited to disclosing the escalation capability — it SHALL NOT instruct the model to attempt restricted operations, SHALL NOT instruct it to refrain, and SHALL NOT restate or claim the sandbox is immutable. Whether to attempt an out-of-box operation SHALL remain the model's own decision.
34
+
35
+ #### Scenario: Text states the lever only
36
+ - **WHEN** the escalation-guidance entry renders under `workspace-write`
37
+ - **THEN** its text discloses the single-call escalation path and contains no imperative coaching sentence (no "do not refuse", no "go try", no "you are sandboxed and cannot change it")
38
+
39
+ ### Requirement: Guidance rides the runtime-context snapshot
40
+ The system SHALL render the escalation guidance inside the same runtime-context snapshot region as the sandbox and approval policy entries (positioned after the approval-policy entry), so it is adjacent to the policy statements the model already reads. The injection SHALL register through the plugin system-prompt context API (`ctx.systemPrompt.context`) without modifying upstream DSH source.
41
+
42
+ #### Scenario: Guidance sits beside the policy entries
43
+ - **WHEN** a `workspace-write` session renders the system prompt
44
+ - **THEN** the escalation-guidance entry appears in the runtime-context snapshot, ordered after the `approval:policy` entry and before any later context entries
@@ -0,0 +1,37 @@
1
+ # fs-scheme-resolution Specification
2
+
3
+ ## Purpose
4
+ 定义文件系统服务层的 URL scheme 解析契约:通过 `urlSchemes` 配置门控,在 `ctx.fs` 实例上原位包裹,使 `dsh://`、`dvc://`、`http(s)://` 等静态 scheme 经统一解析器读出虚拟目标。门控关闭时透明退化为 stock 行为(bit-for-bit 等价),真实路径语义不受影响。
5
+
6
+ ### Requirement: FS-gate scheme resolution
7
+ The system SHALL resolve file-type scheme URLs (`dsh://`, `dvc://`, `http(s)://`) at the filesystem service seam by wrapping the LIVE `ctx.fs` instance in place (instance-level method wrap of its five public methods; symbol-idempotent, re-applied per boot, every added behavior gated behind `urlSchemes`; a wrap failure degrades to the stock service, never a failed boot). Every `ctx.fs` consumer — including the platform's native read/write/edit tools with no tool-layer wrapping — SHALL thus resolve these URLs through the URL resolver: `resolve` returns a virtual target keyed by the URL, `stat` answers a synthesized file entry, and `readText` dereferences the resolved text.
8
+ #### Scenario: Native read of a static scheme without tool-layer participation
9
+ - **WHEN** the hashline tool gate is disabled (the wrapped read delegates to the captured native read) and the model reads `dsh://docs/<doc>`
10
+ - **THEN** the native read resolves the document through the filesystem backend and returns its text — no tool-layer scheme code participated
11
+
12
+ #### Scenario: Real-path passthrough
13
+ - **WHEN** any consumer resolves, reads, or mutates a real filesystem path
14
+ - **THEN** every operation delegates to the inherited sandboxed backend unchanged (containment, observation events, atomic writes, read-match-write critical section)
15
+
16
+ ### Requirement: Virtual targets are read-only
17
+ The backend SHALL refuse mutations that target a virtual scheme path with the structured `FS_VIRTUAL_READONLY` error before any policy or filesystem work, while `urlSchemes` is enabled. Real-path mutations keep the stock fence.
18
+
19
+ #### Scenario: Native write against a virtual target
20
+ - **WHEN** the model writes to a scheme path that has no write channel at the FS layer
21
+ - **THEN** the call fails with `FS_VIRTUAL_READONLY` and no file is created
22
+
23
+ ### Requirement: Session-layer schemes stay at the tool layer
24
+ `ctx://`, `agent://`, and `skill://` require live-agent semantics the filesystem layer does not have: the first two read session state, and `skill://` must consult the host skill registry's layered catalog — whose discovery (which roots load: project/user/preset/custom/bundled, scan depth, rank and scope merge) is business logic owned by the host `skill-filesystem` provider and resolves only against a calling agent. The backend SHALL answer all three with a structured session-layer boundary error naming the sanctioned channel (the native `skill` tool for skill loading), and SHALL NOT re-implement or approximate skill discovery (no own root parsing, no cwd heuristics, no scope guessing). The wrapped read tool's scheme branch SHALL continue to serve `ctx://`/`agent://` with the calling agent's context, and the skill handler at the tool layer SHALL mirror the native tool's lookup exactly (`cwd` from the agent's session header, the agent as viewing scope).
25
+ #### Scenario: Native read of ctx:// names the boundary
26
+ - **WHEN** the model reads a `ctx://` URL through a consumer that has no live-agent resolver environment
27
+ - **THEN** the backend returns a structured error explaining that `ctx://` is served by the session-layer read, instead of a raw filesystem failure
28
+
29
+ #### Scenario: Native read of skill:// names the session-layer boundary
30
+ - **WHEN** the model reads any `skill://` URL through a filesystem-layer consumer (no calling agent)
31
+ - **THEN** the backend returns the structured session-layer boundary error pointing at the native `skill` tool — never a registry verdict (such as "unknown skill") guessed from a deployment-declared cwd
32
+ ### Requirement: Gate disables resolution transparently
33
+ When `urlSchemes` is `false`, the backend SHALL short-circuit every override to the inherited stock behavior — scheme paths fail as ordinary native paths, real-path semantics are untouched, and the deployment behaves bit-for-bit like the stock `fs-sandbox` row.
34
+
35
+ #### Scenario: Stock degradation
36
+ - **WHEN** the deployment sets `urlSchemes: false` on the mounted row and a consumer reads a scheme URL
37
+ - **THEN** the call fails as it would under the stock backend (no scheme interception anywhere)
@@ -0,0 +1,41 @@
1
+ # hash-edit Specification
2
+
3
+ ## Purpose
4
+ DASHR 的哈希锚点读写/edit 契约:`read` 以 `HASH│content` 行锚点呈现文件;`edit`/`undo_last_edit` 以锚点定位做单文件原子编辑;served 账本(schema v7,path 主键,集中存储于 `$DSH_HOME/storages/dsh-better-edit/`)保证锚点跨会话可验证。hash-edit 是自包含模块(`src/hashline/`),对 url-schemes 零出向引用;与 url-schemes 的组合只发生在 `url-schemes/index.ts` 组合根(read 链:scheme wrapper → hashline read → captured native)。
5
+
6
+ ## Requirements
7
+
8
+ ### Requirement: Anchored read presentation
9
+ The hashline read doer SHALL present every served text file as `HASH│content` rows (3-char hash anchors for later edit calls), with paging via `offset`/`limit` on file reads.
10
+
11
+ #### Scenario: Anchored read
12
+ - **WHEN** the agent calls `read` on a filesystem path with hashline enabled
13
+ - **THEN** each line returns as `HASH│content` and the served rows persist to the centralized store keyed by absolute path
14
+
15
+ ### Requirement: Content-anchored edit with single-file atomicity
16
+ The `edit` tool SHALL apply the payload `{ path: string|null, edits: [[remove_from, remove_to, replacement_text], ...] }` as a single-file atomic batch; `null` path infers via anchors; `undo_last_edit` reverts the last edit on a file. Edit verification is content-anchored: a range whose bounds are unseen by the served ledger MAY pass via the content fast-path; `E_RANGE_STALE`/`E_RANGE_UNSERVED` semantics are preserved.
17
+
18
+ #### Scenario: Atomic batch
19
+ - **WHEN** an edit payload contains multiple edits and one anchor fails to resolve
20
+ - **THEN** no edit from the batch is written and the failing range is echoed back with fresh anchors
21
+
22
+ ### Requirement: Host guard independence
23
+ The host `dsh-fs-observation-policy`'s `E_NOT_OBSERVED` (read-before-edit) SHALL remain an independent design-intent guard for cross-session edits; hash-edit verification complements, never replaces, it.
24
+
25
+ #### Scenario: Cross-session edit
26
+ - **WHEN** a session edits a file it never read and no served rows exist for its bounds
27
+ - **THEN** the host observation policy still denies or the content fast-path rules, per its own contract — hash-edit does not silently relax it
28
+
29
+ ### Requirement: Module orthogonality
30
+ The hashline module SHALL NOT import anything from `url-schemes` (or any scheme surface). Its standalone entries — `initHashlineRuntime()`, `createHashlineReadTool()`, `installHashline()` — SHALL work with url-schemes absent; per-agent features it does not own (e.g. lsp edit feedback) arrive only as injected callbacks.
31
+
32
+ #### Scenario: Standalone mount
33
+ - **WHEN** a deployment mounts hashline without url-schemes
34
+ - **THEN** anchored read + edit/undo + guidance register and function; no scheme branch exists anywhere in the module
35
+
36
+ ### Requirement: Shared sandbox controller
37
+ When co-mounted with a write wrapper, the `FsSandboxController` instance SHALL be created by the composition root and injected into both the hashline edit family and the write wrapper, so escalation advertisement and per-call policy stamping share one controller.
38
+
39
+ #### Scenario: Shared escalation surface
40
+ - **WHEN** a confining sandbox backend is mounted and both hashline and the write wrapper are active
41
+ - **THEN** `edit` and `write` escalate through the same controller with the session workspace root stamped