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.
- 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
- 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
- package/docs/specs/agent/spec.md +54 -0
- package/docs/specs/ast/spec.md +34 -0
- package/docs/specs/compaction-recall/spec.md +46 -0
- package/docs/specs/ctx/spec.md +107 -0
- package/docs/specs/dsh/spec.md +47 -0
- package/docs/specs/dvc/spec.md +87 -0
- package/docs/specs/escalation-guidance/spec.md +44 -0
- package/docs/specs/fs-scheme-resolution/spec.md +37 -0
- package/docs/specs/hash-edit/spec.md +41 -0
- package/docs/specs/http-read/spec.md +73 -0
- package/docs/specs/kernel-provisioning/spec.md +53 -0
- package/docs/specs/lsp/spec.md +121 -0
- package/docs/specs/mobile-layout/spec.md +108 -0
- package/docs/specs/model-failover/spec.md +20 -0
- package/docs/specs/preact-ui-shell/spec.md +22 -0
- package/docs/specs/repl-dispatch-resilience/spec.md +21 -0
- package/docs/specs/skill/spec.md +58 -0
- package/docs/specs/tool-surface/spec.md +222 -0
- package/docs/specs/url-schema/spec.md +148 -0
- package/docs/specs/web-trust-fence/spec.md +43 -0
- package/dsh-docs/AGENTS.md +75 -0
- package/dsh-docs/agent-lifecycle.md +84 -0
- package/dsh-docs/agent-lifecycle.zh.md +86 -0
- package/dsh-docs/api-gateway.md +164 -0
- package/dsh-docs/api-gateway.zh.md +164 -0
- package/dsh-docs/architecture.md +150 -0
- package/dsh-docs/architecture.zh.md +154 -0
- package/dsh-docs/capability-seams.md +543 -0
- package/dsh-docs/capability-seams.zh.md +545 -0
- package/dsh-docs/config-catalog.md +3473 -0
- package/dsh-docs/config-catalog.zh.md +3474 -0
- package/dsh-docs/cookbook/adding-a-package.md +117 -0
- package/dsh-docs/cookbook/adding-a-package.zh.md +119 -0
- package/dsh-docs/cookbook/adding-a-remote-api.md +197 -0
- package/dsh-docs/cookbook/adding-a-remote-api.zh.md +197 -0
- package/dsh-docs/cookbook/adding-a-settings-card.md +102 -0
- package/dsh-docs/cookbook/adding-a-settings-card.zh.md +102 -0
- package/dsh-docs/cookbook/adding-a-tool.md +101 -0
- package/dsh-docs/cookbook/adding-a-tool.zh.md +103 -0
- package/dsh-docs/cookbook/adding-a-vendored-package.md +59 -0
- package/dsh-docs/cookbook/adding-a-vendored-package.zh.md +59 -0
- package/dsh-docs/cookbook/adding-an-llm-adapter.md +43 -0
- package/dsh-docs/cookbook/adding-an-llm-adapter.zh.md +43 -0
- package/dsh-docs/cookbook/extension-cookbook.md +132 -0
- package/dsh-docs/cookbook/extension-cookbook.zh.md +136 -0
- package/dsh-docs/cookbook/maintaining-dsh-code-review.md +64 -0
- package/dsh-docs/cookbook/maintaining-dsh-code-review.zh.md +64 -0
- package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.md +32 -0
- package/dsh-docs/cookbook/responding-to-pr-review-on-a-stack.zh.md +32 -0
- package/dsh-docs/cordis-api/context.md +364 -0
- package/dsh-docs/cordis-api/context.zh.md +366 -0
- package/dsh-docs/cordis-api/events.md +207 -0
- package/dsh-docs/cordis-api/events.zh.md +209 -0
- package/dsh-docs/cordis-api/fiber.md +375 -0
- package/dsh-docs/cordis-api/fiber.zh.md +377 -0
- package/dsh-docs/cordis-api/inherited.md +39 -0
- package/dsh-docs/cordis-api/registry.md +152 -0
- package/dsh-docs/cordis-api/registry.zh.md +154 -0
- package/dsh-docs/cordis-api/service.md +102 -0
- package/dsh-docs/cordis-api/service.zh.md +104 -0
- package/dsh-docs/cordis-primer.md +45 -0
- package/dsh-docs/cordis-primer.zh.md +51 -0
- package/dsh-docs/cordis-tutorial/01-first-plugin.md +95 -0
- package/dsh-docs/cordis-tutorial/01-first-plugin.zh.md +95 -0
- package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.md +98 -0
- package/dsh-docs/cordis-tutorial/02-lifecycle-and-effects.zh.md +98 -0
- package/dsh-docs/cordis-tutorial/03-services.md +98 -0
- package/dsh-docs/cordis-tutorial/03-services.zh.md +98 -0
- package/dsh-docs/cordis-tutorial/04-events.md +144 -0
- package/dsh-docs/cordis-tutorial/04-events.zh.md +144 -0
- package/dsh-docs/cordis-tutorial/05-config.md +84 -0
- package/dsh-docs/cordis-tutorial/05-config.zh.md +84 -0
- package/dsh-docs/cordis-tutorial/06-composition-and-hmr.md +113 -0
- package/dsh-docs/cordis-tutorial/06-composition-and-hmr.zh.md +113 -0
- package/dsh-docs/cordis-tutorial/07-into-the-harness.md +108 -0
- package/dsh-docs/cordis-tutorial/07-into-the-harness.zh.md +108 -0
- package/dsh-docs/cordis-tutorial/index.md +60 -0
- package/dsh-docs/cordis-tutorial/index.zh.md +62 -0
- package/dsh-docs/deepseek-llm-api-wire-extensions.md +163 -0
- package/dsh-docs/deepseek-llm-api-wire-extensions.zh.md +163 -0
- package/dsh-docs/defensive-patterns.md +33 -0
- package/dsh-docs/defensive-patterns.zh.md +35 -0
- package/dsh-docs/development.md +167 -0
- package/dsh-docs/development.zh.md +173 -0
- package/dsh-docs/event-producer-consumer.md +86 -0
- package/dsh-docs/event-producer-consumer.zh.md +88 -0
- package/dsh-docs/glossary.md +45 -0
- package/dsh-docs/glossary.zh.md +45 -0
- package/dsh-docs/graph-atlas.md +22 -0
- package/dsh-docs/graph-atlas.zh.md +24 -0
- package/dsh-docs/i18n/README.md +60 -0
- package/dsh-docs/i18n/README.zh.md +62 -0
- package/dsh-docs/i18n/style-samples.md +87 -0
- package/dsh-docs/i18n/terminology.md +214 -0
- package/dsh-docs/i18n/translation-prompt.md +263 -0
- package/dsh-docs/i18n/translation-rules.md +69 -0
- package/dsh-docs/i18n/translation-rules.zh.md +69 -0
- package/dsh-docs/module-graph.md +1411 -0
- package/dsh-docs/module-graph.zh.md +1413 -0
- package/dsh-docs/persistence-catalog.md +1075 -0
- package/dsh-docs/persistence-catalog.zh.md +1077 -0
- package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.md +113 -0
- package/dsh-docs/postmortem/0001-acp-default-export-drops-inject.zh.md +113 -0
- package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.md +47 -0
- package/dsh-docs/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +47 -0
- package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.md +53 -0
- package/dsh-docs/postmortem/0003-web-agent-gui-feedback-loop.zh.md +53 -0
- package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +55 -0
- package/dsh-docs/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +55 -0
- package/dsh-docs/postmortem/README.md +18 -0
- package/dsh-docs/postmortem/README.zh.md +18 -0
- package/dsh-docs/rescope.md +53 -0
- package/dsh-docs/rescope.zh.md +53 -0
- package/dsh-docs/subsystems/README.md +61 -0
- package/dsh-docs/subsystems/README.zh.md +61 -0
- package/dsh-docs/subsystems/agent-team.md +207 -0
- package/dsh-docs/subsystems/agent-team.zh.md +207 -0
- package/dsh-docs/subsystems/approval.md +170 -0
- package/dsh-docs/subsystems/approval.zh.md +170 -0
- package/dsh-docs/subsystems/attachment.md +351 -0
- package/dsh-docs/subsystems/attachment.zh.md +351 -0
- package/dsh-docs/subsystems/client-modules.md +168 -0
- package/dsh-docs/subsystems/client-modules.zh.md +168 -0
- package/dsh-docs/subsystems/code-runtime.md +195 -0
- package/dsh-docs/subsystems/code-runtime.zh.md +195 -0
- package/dsh-docs/subsystems/commands.md +219 -0
- package/dsh-docs/subsystems/commands.zh.md +219 -0
- package/dsh-docs/subsystems/compaction.md +238 -0
- package/dsh-docs/subsystems/compaction.zh.md +238 -0
- package/dsh-docs/subsystems/conversation.md +258 -0
- package/dsh-docs/subsystems/conversation.zh.md +258 -0
- package/dsh-docs/subsystems/core.md +1209 -0
- package/dsh-docs/subsystems/core.zh.md +1219 -0
- package/dsh-docs/subsystems/credentials.md +329 -0
- package/dsh-docs/subsystems/credentials.zh.md +329 -0
- package/dsh-docs/subsystems/extensions.md +382 -0
- package/dsh-docs/subsystems/extensions.zh.md +382 -0
- package/dsh-docs/subsystems/feedback.md +266 -0
- package/dsh-docs/subsystems/feedback.zh.md +266 -0
- package/dsh-docs/subsystems/filesystem.md +505 -0
- package/dsh-docs/subsystems/filesystem.zh.md +505 -0
- package/dsh-docs/subsystems/goal.md +277 -0
- package/dsh-docs/subsystems/goal.zh.md +277 -0
- package/dsh-docs/subsystems/invariants.md +88 -0
- package/dsh-docs/subsystems/invariants.zh.md +88 -0
- package/dsh-docs/subsystems/jobs.md +290 -0
- package/dsh-docs/subsystems/jobs.zh.md +290 -0
- package/dsh-docs/subsystems/llm-streaming.md +1080 -0
- package/dsh-docs/subsystems/llm-streaming.zh.md +1086 -0
- package/dsh-docs/subsystems/lsp.md +202 -0
- package/dsh-docs/subsystems/lsp.zh.md +202 -0
- package/dsh-docs/subsystems/permission-presets.md +131 -0
- package/dsh-docs/subsystems/permission-presets.zh.md +131 -0
- package/dsh-docs/subsystems/persistence.md +395 -0
- package/dsh-docs/subsystems/persistence.zh.md +395 -0
- package/dsh-docs/subsystems/plan.md +87 -0
- package/dsh-docs/subsystems/plan.zh.md +87 -0
- package/dsh-docs/subsystems/sandbox.md +220 -0
- package/dsh-docs/subsystems/sandbox.zh.md +220 -0
- package/dsh-docs/subsystems/schedule.md +192 -0
- package/dsh-docs/subsystems/schedule.zh.md +192 -0
- package/dsh-docs/subsystems/scope.md +59 -0
- package/dsh-docs/subsystems/scope.zh.md +59 -0
- package/dsh-docs/subsystems/session-projection.md +354 -0
- package/dsh-docs/subsystems/session-projection.zh.md +354 -0
- package/dsh-docs/subsystems/session-query.md +509 -0
- package/dsh-docs/subsystems/session-query.zh.md +509 -0
- package/dsh-docs/subsystems/session-reference.md +219 -0
- package/dsh-docs/subsystems/session-reference.zh.md +219 -0
- package/dsh-docs/subsystems/session-telemetry.md +194 -0
- package/dsh-docs/subsystems/session-telemetry.zh.md +194 -0
- package/dsh-docs/subsystems/session-title.md +204 -0
- package/dsh-docs/subsystems/session-title.zh.md +204 -0
- package/dsh-docs/subsystems/session.md +1155 -0
- package/dsh-docs/subsystems/session.zh.md +1159 -0
- package/dsh-docs/subsystems/settings.md +405 -0
- package/dsh-docs/subsystems/settings.zh.md +405 -0
- package/dsh-docs/subsystems/shell.md +303 -0
- package/dsh-docs/subsystems/shell.zh.md +303 -0
- package/dsh-docs/subsystems/skills.md +354 -0
- package/dsh-docs/subsystems/skills.zh.md +354 -0
- package/dsh-docs/subsystems/slots.md +175 -0
- package/dsh-docs/subsystems/slots.zh.md +175 -0
- package/dsh-docs/subsystems/spill.md +117 -0
- package/dsh-docs/subsystems/spill.zh.md +117 -0
- package/dsh-docs/subsystems/storage.md +260 -0
- package/dsh-docs/subsystems/storage.zh.md +260 -0
- package/dsh-docs/subsystems/subagent.md +766 -0
- package/dsh-docs/subsystems/subagent.zh.md +770 -0
- package/dsh-docs/subsystems/subprocess.md +324 -0
- package/dsh-docs/subsystems/subprocess.zh.md +324 -0
- package/dsh-docs/subsystems/system-prompt.md +220 -0
- package/dsh-docs/subsystems/system-prompt.zh.md +220 -0
- package/dsh-docs/subsystems/terminal.md +184 -0
- package/dsh-docs/subsystems/terminal.zh.md +184 -0
- package/dsh-docs/subsystems/todo.md +32 -0
- package/dsh-docs/subsystems/todo.zh.md +32 -0
- package/dsh-docs/subsystems/token-meter.md +105 -0
- package/dsh-docs/subsystems/token-meter.zh.md +105 -0
- package/dsh-docs/subsystems/tools.md +720 -0
- package/dsh-docs/subsystems/tools.zh.md +720 -0
- package/dsh-docs/subsystems/typert.md +343 -0
- package/dsh-docs/subsystems/typert.zh.md +343 -0
- package/dsh-docs/subsystems/user-questions.md +178 -0
- package/dsh-docs/subsystems/user-questions.zh.md +178 -0
- package/dsh-docs/subsystems/web-client.md +95 -0
- package/dsh-docs/subsystems/web-client.zh.md +95 -0
- package/dsh-docs/subsystems/web-server.md +154 -0
- package/dsh-docs/subsystems/web-server.zh.md +154 -0
- package/dsh-docs/subsystems/web.md +206 -0
- package/dsh-docs/subsystems/web.zh.md +206 -0
- package/dsh-docs/subsystems/webhook.md +70 -0
- package/dsh-docs/subsystems/webhook.zh.md +70 -0
- package/dsh-docs/subsystems/workflow.md +278 -0
- package/dsh-docs/subsystems/workflow.zh.md +278 -0
- package/dsh-docs/subsystems/workspace.md +321 -0
- package/dsh-docs/subsystems/workspace.zh.md +321 -0
- package/dsh-docs/testing.md +54 -0
- package/dsh-docs/testing.zh.md +54 -0
- package/dsh-docs/tool-catalog.md +2225 -0
- package/dsh-docs/tool-catalog.zh.md +2233 -0
- package/dsh-docs/tool-execution-pipeline.md +62 -0
- package/dsh-docs/tool-execution-pipeline.zh.md +64 -0
- package/dsh-docs/user/develop/basic/config.md +106 -0
- package/dsh-docs/user/develop/basic/config.zh.md +106 -0
- package/dsh-docs/user/develop/basic/index.md +144 -0
- package/dsh-docs/user/develop/basic/index.zh.md +144 -0
- package/dsh-docs/user/develop/basic/publish.md +183 -0
- package/dsh-docs/user/develop/basic/publish.zh.md +183 -0
- package/dsh-docs/user/develop/basic/tool.md +52 -0
- package/dsh-docs/user/develop/basic/tool.zh.md +52 -0
- package/dsh-docs/user/develop/framework/events.md +143 -0
- package/dsh-docs/user/develop/framework/events.zh.md +143 -0
- package/dsh-docs/user/develop/framework/index.md +137 -0
- package/dsh-docs/user/develop/framework/index.zh.md +137 -0
- package/dsh-docs/user/develop/framework/service.md +148 -0
- package/dsh-docs/user/develop/framework/service.zh.md +150 -0
- package/dsh-docs/user/develop/practice/dynamic-cordis.md +15 -0
- package/dsh-docs/user/develop/practice/dynamic-cordis.zh.md +15 -0
- package/dsh-docs/user/develop/practice/index.md +155 -0
- package/dsh-docs/user/develop/practice/index.zh.md +155 -0
- package/dsh-docs/user/develop/practice/llm-adapter.md +189 -0
- package/dsh-docs/user/develop/practice/llm-adapter.zh.md +189 -0
- package/dsh-docs/user/guide/github-review.md +102 -0
- package/dsh-docs/user/guide/github-review.zh.md +102 -0
- package/dsh-docs/user/guide/index.md +30 -0
- package/dsh-docs/user/guide/index.zh.md +30 -0
- package/dsh-docs/user/guide/mcp-memory.md +101 -0
- package/dsh-docs/user/guide/mcp-memory.zh.md +101 -0
- package/dsh-docs/user/guide/network-proxy.md +85 -0
- package/dsh-docs/user/guide/network-proxy.zh.md +85 -0
- package/dsh-docs/user/guide/providers.md +190 -0
- package/dsh-docs/user/guide/providers.zh.md +190 -0
- package/dsh-docs/user/guide/python-sdk.md +150 -0
- package/dsh-docs/user/guide/python-sdk.zh.md +150 -0
- package/dsh-docs/user/guide/schedule.md +21 -0
- package/dsh-docs/user/guide/schedule.zh.md +21 -0
- package/dsh-docs/user/index.md +11 -0
- package/dsh-docs/user/index.zh.md +11 -0
- package/dsh-docs/web-styling.md +29 -0
- package/dsh-docs/web-styling.zh.md +29 -0
- package/lib/client/index.js +268 -38
- package/lib/fs-aware/sandbox-plugin.js +1 -1
- package/lib/index.js +1112 -1261
- package/lib/lsp-server-registry-B8DNonhS.js +3 -0
- package/lib/lsp-server-registry-BexQagaK.js +943 -0
- package/lib/{wrap-DC8O3SYz.js → wrap-JFjcWwZf.js} +42 -16
- 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
|
|
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
|