dsh-tower 1.2.0 → 1.3.0
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/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +36 -0
- package/README.en.md +46 -23
- package/README.md +42 -21
- package/SECURITY.md +20 -0
- package/cordis.patch.yml +22 -0
- package/docs/README.md +13 -0
- package/docs/architecture/overview.md +66 -0
- package/docs/examples/first-mission.md +58 -0
- package/docs/faq.md +43 -0
- package/docs/guide/install-upgrade-migration.md +48 -0
- package/docs/guide/quickstart.md +65 -0
- package/docs/operations/runbook.md +57 -0
- package/docs/operations/security.md +39 -0
- package/docs/reference/blockers.md +50 -0
- package/docs/reference/config.md +51 -0
- package/docs/reference/tools.md +33 -0
- package/index.js +33 -37
- package/lib/config.js +119 -0
- package/lib/modeStore.js +19 -15
- package/lib/tools.js +72 -10
- package/lib/tower.js +74 -5
- package/package.json +18 -4
- package/reviewer.js +39 -0
- package/skills/tower-recovery.md +40 -0
- package/skills/tower-review.md +14 -0
- package/skills/tower-worker-protocol.md +13 -0
- package/skills.js +63 -0
- package/worker.js +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.3.0 — 2026-10-18
|
|
4
|
+
|
|
5
|
+
- **独立的只读 reviewer preset**:新增 `reviewer.js` / `tower-reviewer`,去掉 `write`/`edit`/`pwsh`/
|
|
6
|
+
`tower_mission`/`tower_complete`,只保留读、`tower_diff`、消息、finding、review;`tower_spawn`
|
|
7
|
+
按 role 映射 preset,reviewer 必须等待 mission `completed`。`tower_review` 会记录 reviewer spawn
|
|
8
|
+
时的 tip,审查期间改动分支将被拒绝(修复自审风险)。
|
|
9
|
+
- **新增 `tower_diff`**:只读 merge-base diff(files/commits/patch),供 reviewer 使用。
|
|
10
|
+
- **声明 `Config` schema**:`section`/`allowAgentControl` 兼容别名 + `mode`/`review`/`merge`/
|
|
11
|
+
`worktrees`/`spawn` 分组;评审轮数、合并超时/缓冲、worktree 根、spawn preset/model 等可配置;
|
|
12
|
+
`mode.storePath`/`maxEntries`/`autoPauseGoal`/`denyTools`/`sectionAppend` 生效。
|
|
13
|
+
- **spawn 健壮性**:preset 存在性预检、按 role 选择 preset/model、失败/空闲状态写回 roster。
|
|
14
|
+
- **内置 Agent Skills**:`skills/tower-worker-protocol`、`skills/tower-review`、`skills/tower-recovery`,
|
|
15
|
+
通过 `dsh-tower/skills` provider 注册;worker/reviewer preset 允许 `skill`。
|
|
16
|
+
- **文档**:新增 `docs/`(quickstart、安装升级迁移、工具/配置/blocker 参考、架构、运维、安全、FAQ、
|
|
17
|
+
示例);README quick start 修正为“先 reviewer 后 preflight 全绿”,工具数/测试数/blocker 数修正;
|
|
18
|
+
`npm run docs:check` 与 CI 校验文档与代码一致。
|
|
19
|
+
- `package.json` 增加 `@deepseek-ai/schemastery` 依赖、`files` 补充 reviewer/skills/docs/贡献与安全文档。
|
|
20
|
+
- 测试 26 项(新增 preset/skills/config 四个用例)。
|
|
21
|
+
|
|
3
22
|
## 1.2.0 — 2026-10-18
|
|
4
23
|
|
|
5
24
|
- **`tower_merge` 对“提交已落地但调用报错”的恢复**:合并命令失败/超时后检查 `HEAD`,若已是
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in `dsh-tower`. This is a DeepSeek Harness plugin; the repository root is the bundle itself (`package.json` + `cordis.patch.yml`).
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/louifinn/dsh-tower.git
|
|
9
|
+
cd dsh-tower
|
|
10
|
+
npm test # no dependencies to install; Node's built-in test runner only
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requirements: Node.js 22+, Git. No package installation is needed for the offline suite.
|
|
14
|
+
|
|
15
|
+
## What to change
|
|
16
|
+
|
|
17
|
+
- `index.js` — Host entry: `/tower` command, mode guard, dynamic prompt section, `tower_control`.
|
|
18
|
+
- `lib/` — the Tower domain (`tower.js`), tool definitions (`tools.js`), persistence (`store.js`, `modeStore.js`), Git helpers (`git.js`).
|
|
19
|
+
- `worker.js` — the `tower-worker` preset policy (tool surface + protocol text).
|
|
20
|
+
- `tests/` — add a regression test for every behavior change. The offline suite is `tests/*.test.mjs`; live-runtime scripts are documented in `tests/README.md`.
|
|
21
|
+
|
|
22
|
+
## Pull request checklist
|
|
23
|
+
|
|
24
|
+
1. `npm test` passes locally (26 tests today, including protocol end-to-end against a real temporary Git repository) and `npm run docs:check` passes.
|
|
25
|
+
2. New behavior has a test. Bug fixes should add a test that fails before the fix.
|
|
26
|
+
3. No secrets, tokens, personal paths, session logs, or generated artifacts are added. `.gitignore` covers local acceptance output; do not force-add it.
|
|
27
|
+
4. Keep the public API and state-file formats compatible; if a change is breaking, call it out in `CHANGELOG.md` and bump the version.
|
|
28
|
+
5. Update `README.md` / `README.en.md` when the user-visible behavior changes.
|
|
29
|
+
|
|
30
|
+
## Commit style
|
|
31
|
+
|
|
32
|
+
Conventional-commit prefixes are preferred: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`. Keep the subject line in the imperative mood.
|
|
33
|
+
|
|
34
|
+
## Reporting issues
|
|
35
|
+
|
|
36
|
+
Include the DSH version, Node.js version, the exact `/tower` or tool input, and the relevant error text. Redact paths, tokens, session ids, and any personal data from logs before posting. For security issues, see [SECURITY.md](SECURITY.md).
|
package/README.en.md
CHANGED
|
@@ -50,24 +50,23 @@ The Host plugin is loaded when the DSH process starts, so **restart DSH after in
|
|
|
50
50
|
|
|
51
51
|
## Quick start
|
|
52
52
|
|
|
53
|
-
```
|
|
53
|
+
```text
|
|
54
54
|
/tower on
|
|
55
55
|
|
|
56
|
-
tower_init
|
|
57
|
-
tower_plan
|
|
58
|
-
tower_spawn
|
|
59
|
-
# worker implements and
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
tower_teardown <repoRoot>
|
|
56
|
+
tower_init repoRoot=D:\work\repo # records the base (current branch by default)
|
|
57
|
+
tower_plan repoRoot=D:\work\repo missions=[{"title":"Feature A","scope":["src/a/**"],"tasks":["implement","commit"],"kind":"build"}]
|
|
58
|
+
tower_spawn repoRoot=D:\work\repo missionId=M1 role=worker
|
|
59
|
+
# the worker implements and commits in its worktree, calls tower_complete; its final report returns here
|
|
60
|
+
tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
|
|
61
|
+
# the reviewer inspects read-only and calls tower_review (clean+merge, or p1-Nitems/p2-Nitems)
|
|
62
|
+
tower_preflight repoRoot=D:\work\repo missionId=M1 # expect no-review before review; ok:true after it
|
|
63
|
+
tower_merge repoRoot=D:\work\repo missionId=M1
|
|
64
|
+
tower_teardown repoRoot=D:\work\repo
|
|
66
65
|
|
|
67
66
|
/tower off
|
|
68
67
|
```
|
|
69
68
|
|
|
70
|
-
A
|
|
69
|
+
A small change usually takes one worker mission and one reviewer round. A full copy-paste example is in [docs/examples/first-mission.md](docs/examples/first-mission.md); per-tool parameters are in [docs/reference/tools.md](docs/reference/tools.md).
|
|
71
70
|
|
|
72
71
|
## Commands
|
|
73
72
|
|
|
@@ -88,28 +87,42 @@ Image and file attachments are forwarded together with the instruction as one co
|
|
|
88
87
|
- id: tower
|
|
89
88
|
name: 'dsh-tower'
|
|
90
89
|
config:
|
|
91
|
-
section: 'Optional
|
|
92
|
-
allowAgentControl: true #
|
|
90
|
+
section: 'Optional legacy alias replacing the Tower-mode prompt text'
|
|
91
|
+
allowAgentControl: true # legacy alias for mode.allowAgentControl
|
|
92
|
+
mode:
|
|
93
|
+
allowAgentControl: true
|
|
94
|
+
autoPauseGoal: true
|
|
95
|
+
denyTools: [create_goal, todo_write, team_task_create, team_task_update, team_task_reopen, spawn_teammate]
|
|
96
|
+
review:
|
|
97
|
+
maxRoundsPerReviewer: 5
|
|
98
|
+
merge:
|
|
99
|
+
timeoutMs: 900000
|
|
100
|
+
worktrees:
|
|
101
|
+
root: '' # empty = <checkout parent>/.dsh-tower-worktrees/<hash>
|
|
102
|
+
spawn:
|
|
103
|
+
workerPreset: tower-worker
|
|
104
|
+
reviewerPreset: tower-reviewer
|
|
105
|
+
maxDepth: 1
|
|
93
106
|
```
|
|
94
107
|
|
|
95
|
-
- `allowAgentControl` defaults to `false` in code
|
|
96
|
-
-
|
|
108
|
+
- `mode.allowAgentControl` defaults to `false` in code; the bundled `cordis.patch.yml` enables it (`true`) so the model may enter/leave when the user intent or task state warrants it, always stating a reason.
|
|
109
|
+
- Every key and default is in [docs/reference/config.md](docs/reference/config.md); unknown keys are rejected so typos cannot silently disable security-relevant settings.
|
|
97
110
|
|
|
98
|
-
## Tools (
|
|
111
|
+
## Tools (18)
|
|
99
112
|
|
|
100
|
-
`tower_init` · `tower_plan` · `tower_mission` · `tower_status` · `tower_spawn` · `tower_roster` · `tower_adopt` · `tower_send` · `tower_inbox` · `tower_complete` · `tower_finding` · `tower_review` · `tower_preflight` · `tower_merge` · `tower_rebase` · `tower_teardown` · `tower_mode`
|
|
113
|
+
`tower_init` · `tower_plan` · `tower_mission` · `tower_status` · `tower_spawn` · `tower_roster` · `tower_adopt` · `tower_diff` · `tower_send` · `tower_inbox` · `tower_complete` · `tower_finding` · `tower_review` · `tower_preflight` · `tower_merge` · `tower_rebase` · `tower_teardown` · `tower_mode`
|
|
101
114
|
|
|
102
|
-
`tower_control` exists only when `allowAgentControl: true
|
|
115
|
+
`tower_control` exists only when `allowAgentControl: true` (the bundled patch enables it, so an installed build exposes 19 tools).
|
|
103
116
|
|
|
104
117
|
## Safety and guardrails
|
|
105
118
|
|
|
106
119
|
- **No merge without a green gate.** `tower_preflight` is read-only and reports stable blocker codes; `tower_merge` refuses when any blocker remains.
|
|
107
|
-
- **Review is bound to a commit.**
|
|
120
|
+
- **Review is bound to a commit.** A moved tip reports `tip-moved` and requires a fresh review; reviewers use a read-only preset, and a branch change during review is rejected.
|
|
108
121
|
- **Scope enforcement.** A mission may only change files matched by its declared scope globs; `out-of-scope` blocks the merge.
|
|
109
122
|
- **Worktree ownership.** Dirty worktrees, a running worker, or a branch owned by a closed mission block teardown/merge.
|
|
110
123
|
- **Goal isolation.** Entering Tower mode pauses the active goal and records it; leaving does not resume it. Use `/tower off` and resume the goal yourself.
|
|
111
124
|
|
|
112
|
-
|
|
125
|
+
13 stable blocker codes (plus 6 additional gate guards and 6 merge/rebase failure codes) — the complete list with triggers and fixes is in [docs/reference/blockers.md](docs/reference/blockers.md).
|
|
113
126
|
|
|
114
127
|
## How it works
|
|
115
128
|
|
|
@@ -122,14 +135,16 @@ A mission moves through `planned → active → completed → merged` (or `block
|
|
|
122
135
|
| `<checkout parent>/.dsh-tower-worktrees/<hash>/<missionId>` | per-mission worktree |
|
|
123
136
|
| `$DSH_HOME/tower-mode/modes.json` | Tower mode state per agent/session |
|
|
124
137
|
|
|
125
|
-
Workers
|
|
138
|
+
Workers use the bundled `tower-worker` preset (writable worktree surface); reviewers use the separate read-only `tower-reviewer` preset and can only inspect (`tower_diff`) and submit `tower_review`. The reviewer records the branch tip at spawn; if the branch moves, the review is rejected. Neither preset can merge or spawn.
|
|
126
139
|
|
|
127
140
|
## Project layout
|
|
128
141
|
|
|
129
142
|
| Path | Contents |
|
|
130
143
|
|---|---|
|
|
131
144
|
| `package.json`, `cordis.patch.yml` | bundle manifest and loader patch (inserts the Host plugin and the `tower-worker` preset) |
|
|
132
|
-
| `index.js`, `lib/`, `worker.js` | Host entry, protocol implementation, worker preset
|
|
145
|
+
| `index.js`, `lib/`, `worker.js`, `reviewer.js` | Host entry, protocol/config implementation, worker and reviewer preset policies |
|
|
146
|
+
| `skills.js`, `skills/` | bundled agent skills (worker protocol, review checklist, recovery playbook) |
|
|
147
|
+
| `docs/` | guides, reference, architecture, operations, FAQ and examples |
|
|
133
148
|
| `locale/`, `icon.svg` | localization and icon |
|
|
134
149
|
| `tests/` | pure-function, protocol end-to-end, and plugin-level tests; optional isolated Web-runtime scripts |
|
|
135
150
|
| `ui-bundle/` | optional Client widget (static `/tower` hint), not installed by default |
|
|
@@ -143,6 +158,14 @@ npm test # node --test tests/*.test.mjs
|
|
|
143
158
|
|
|
144
159
|
The offline suite needs no DSH process and creates throwaway Git repositories in the OS temp directory. `tests/README.md` documents the isolated Web-runtime checks, model-driven acceptance, and the session-diagnostic scripts used while hardening the plugin.
|
|
145
160
|
|
|
161
|
+
## Documentation
|
|
162
|
+
|
|
163
|
+
- [docs/README.md](docs/README.md) — documentation index (new user / operator / maintainer paths)
|
|
164
|
+
- [Quick start](docs/guide/quickstart.md) · [Install, upgrade and migration](docs/guide/install-upgrade-migration.md)
|
|
165
|
+
- [Tools reference](docs/reference/tools.md) · [Config reference](docs/reference/config.md) · [Blockers and failure codes](docs/reference/blockers.md)
|
|
166
|
+
- [Architecture and state machine](docs/architecture/overview.md) · [Operations runbook](docs/operations/runbook.md) · [Security model](docs/operations/security.md)
|
|
167
|
+
- [FAQ](docs/faq.md) · [Examples](docs/examples/first-mission.md)
|
|
168
|
+
|
|
146
169
|
## Troubleshooting
|
|
147
170
|
|
|
148
171
|
- **The plugin does not appear after install/update.** Host plugins load at DSH startup; restart DSH. Confirm the entry point targets this repository root (the folder with `package.json` and `cordis.patch.yml`), not a subdirectory.
|
package/README.md
CHANGED
|
@@ -50,24 +50,23 @@ Host 插件在 DSH 进程启动时加载,**安装或更新后需重启 DSH**
|
|
|
50
50
|
|
|
51
51
|
## 快速开始
|
|
52
52
|
|
|
53
|
-
```
|
|
53
|
+
```text
|
|
54
54
|
/tower on
|
|
55
55
|
|
|
56
|
-
tower_init
|
|
57
|
-
tower_plan
|
|
58
|
-
tower_spawn
|
|
59
|
-
# worker
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
tower_teardown <repoRoot>
|
|
56
|
+
tower_init repoRoot=D:\work\repo # 首次初始化,记录 base(默认当前分支)
|
|
57
|
+
tower_plan repoRoot=D:\work\repo missions=[{"title":"Feature A","scope":["src/a/**"],"tasks":["implement","commit"],"kind":"build"}]
|
|
58
|
+
tower_spawn repoRoot=D:\work\repo missionId=M1 role=worker
|
|
59
|
+
# worker 在 worktree 内实现并提交,调用 tower_complete;最终报告会返回本会话
|
|
60
|
+
tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
|
|
61
|
+
# reviewer 只读审查并调用 tower_review(clean+merge 或 p1-Nitems/p2-Nitems)
|
|
62
|
+
tower_preflight repoRoot=D:\work\repo missionId=M1 # 审查前 预期 no-review;审查后应为 ok:true
|
|
63
|
+
tower_merge repoRoot=D:\work\repo missionId=M1
|
|
64
|
+
tower_teardown repoRoot=D:\work\repo
|
|
66
65
|
|
|
67
66
|
/tower off
|
|
68
67
|
```
|
|
69
68
|
|
|
70
|
-
小改动通常一轮 worker + 一轮 reviewer
|
|
69
|
+
小改动通常一轮 worker + 一轮 reviewer 即可完成。完整可复制示例见 [docs/examples/first-mission.md](docs/examples/first-mission.md),逐工具参数见 [docs/reference/tools.md](docs/reference/tools.md)。
|
|
71
70
|
|
|
72
71
|
## 命令
|
|
73
72
|
|
|
@@ -88,28 +87,42 @@ tower_teardown <repoRoot>
|
|
|
88
87
|
- id: tower
|
|
89
88
|
name: 'dsh-tower'
|
|
90
89
|
config:
|
|
91
|
-
section: '
|
|
92
|
-
allowAgentControl: true #
|
|
90
|
+
section: '(可选,兼容别名)覆盖 Tower 模式系统提示文案'
|
|
91
|
+
allowAgentControl: true # 兼容别名;等价于 mode.allowAgentControl
|
|
92
|
+
mode:
|
|
93
|
+
allowAgentControl: true
|
|
94
|
+
autoPauseGoal: true
|
|
95
|
+
denyTools: [create_goal, todo_write, team_task_create, team_task_update, team_task_reopen, spawn_teammate]
|
|
96
|
+
review:
|
|
97
|
+
maxRoundsPerReviewer: 5
|
|
98
|
+
merge:
|
|
99
|
+
timeoutMs: 900000
|
|
100
|
+
worktrees:
|
|
101
|
+
root: '' # 空 = <主检出父目录>/.dsh-tower-worktrees/<hash>
|
|
102
|
+
spawn:
|
|
103
|
+
workerPreset: tower-worker
|
|
104
|
+
reviewerPreset: tower-reviewer
|
|
105
|
+
maxDepth: 1
|
|
93
106
|
```
|
|
94
107
|
|
|
95
|
-
- 代码中 `allowAgentControl` 默认 `false
|
|
96
|
-
-
|
|
108
|
+
- 代码中 `mode.allowAgentControl` 默认 `false`;随包 `cordis.patch.yml` 设为 `true`:模型可在用户意图/任务状态明确时自行进出,但必须给出理由;设为 `false` 时只有人类能切换。
|
|
109
|
+
- 所有键与默认值见 [docs/reference/config.md](docs/reference/config.md);未知键会被拒绝(防止拼写错误静默失效)。
|
|
97
110
|
|
|
98
|
-
## 工具(
|
|
111
|
+
## 工具(18 个)
|
|
99
112
|
|
|
100
|
-
`tower_init` · `tower_plan` · `tower_mission` · `tower_status` · `tower_spawn` · `tower_roster` · `tower_adopt` · `tower_send` · `tower_inbox` · `tower_complete` · `tower_finding` · `tower_review` · `tower_preflight` · `tower_merge` · `tower_rebase` · `tower_teardown` · `tower_mode`
|
|
113
|
+
`tower_init` · `tower_plan` · `tower_mission` · `tower_status` · `tower_spawn` · `tower_roster` · `tower_adopt` · `tower_diff` · `tower_send` · `tower_inbox` · `tower_complete` · `tower_finding` · `tower_review` · `tower_preflight` · `tower_merge` · `tower_rebase` · `tower_teardown` · `tower_mode`
|
|
101
114
|
|
|
102
|
-
`tower_control` 仅在 `allowAgentControl: true`
|
|
115
|
+
`tower_control` 仅在 `allowAgentControl: true` 时存在(随包 patch 默认开启,因此安装后共 19 个工具)。
|
|
103
116
|
|
|
104
117
|
## 安全与约束
|
|
105
118
|
|
|
106
119
|
- **门禁不全绿不合并。** `tower_preflight` 是只读检查,返回稳定的 blocker code;存在 blocker 时 `tower_merge` 拒绝合并。
|
|
107
|
-
- **审查绑定提交。** 审查后分支 tip 变化会报 `tip-moved
|
|
120
|
+
- **审查绑定提交。** 审查后分支 tip 变化会报 `tip-moved`,必须重新审查;reviewer 使用只读 preset,若在审查期间改动分支,提交审查会被拒绝。
|
|
108
121
|
- **scope 强制。** mission 只能修改其 scope glob 匹配的文件,越界会以 `out-of-scope` 阻止合并。
|
|
109
122
|
- **worktree 归属。** 脏工作树、运行中的 worker、已关闭 mission 的分支都会阻止合并或 teardown。
|
|
110
123
|
- **goal 隔离。** 进入 Tower 模式会暂停活跃 goal 并记录;退出不会自动恢复,需要你自行恢复。
|
|
111
124
|
|
|
112
|
-
13 个稳定 blocker code
|
|
125
|
+
13 个稳定 blocker code(另有 6 个 gate 守卫与 6 个 merge/rebase 操作失败码)——完整清单、触发条件与修复动作见 [docs/reference/blockers.md](docs/reference/blockers.md)。
|
|
113
126
|
|
|
114
127
|
## 工作原理
|
|
115
128
|
|
|
@@ -143,6 +156,14 @@ npm test # node --test tests/*.test.mjs
|
|
|
143
156
|
|
|
144
157
|
离线套件不需要 DSH 进程,只在系统临时目录创建一次性 Git 仓库。隔离 Web 运行时检查、模型驱动验收和会话诊断脚本见 `tests/README.md`。
|
|
145
158
|
|
|
159
|
+
## 文档
|
|
160
|
+
|
|
161
|
+
- [docs/README.md](docs/README.md) — 文档索引(新用户 / 运维 / 维护者三条阅读路径)
|
|
162
|
+
- [快速开始](docs/guide/quickstart.md) · [安装、升级与迁移](docs/guide/install-upgrade-migration.md)
|
|
163
|
+
- [工具参考](docs/reference/tools.md) · [配置参考](docs/reference/config.md) · [blocker 与失败码](docs/reference/blockers.md)
|
|
164
|
+
- [架构与状态机](docs/architecture/overview.md) · [运维 runbook](docs/operations/runbook.md) · [安全模型](docs/operations/security.md)
|
|
165
|
+
- [FAQ](docs/faq.md) · [示例](docs/examples/first-mission.md)
|
|
166
|
+
|
|
146
167
|
## 常见问题
|
|
147
168
|
|
|
148
169
|
- **安装/更新后插件不出现。** Host 插件在 DSH 启动时加载,请重启 DSH;并确认安装目标指向本仓库根目录(含 `package.json` 与 `cordis.patch.yml` 的那一层),而不是子目录。
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
`dsh-tower` runs inside a local DeepSeek Harness process. It does not send data to any network service of its own: mission state is written under the target repository's `.git/dsh-tower/`, and Tower-mode state is written under `$DSH_HOME/tower-mode/`.
|
|
6
|
+
|
|
7
|
+
Areas that matter for security reports:
|
|
8
|
+
|
|
9
|
+
- The merge gate (`tower_preflight` / `tower_merge`): bypassing scope, review, staleness, or ownership checks.
|
|
10
|
+
- The mode guard: reaching a denied tool (`create_goal`, `todo_write`, Team task tools, `spawn_teammate`, `update_goal(resume)`) while Tower mode is active.
|
|
11
|
+
- Command forwarding: injecting content into the model history that the user did not send.
|
|
12
|
+
- Writes outside the declared scope, the mission worktree, or the DSH home directory.
|
|
13
|
+
|
|
14
|
+
## Reporting
|
|
15
|
+
|
|
16
|
+
Please do **not** publish exploit details in a public issue. Open a minimal issue titled `security: request private contact` describing only the impact category (for example “merge gate bypass”), and a maintainer will arrange a private channel. Include the affected version and, if possible, a minimal reproduction in the private follow-up.
|
|
17
|
+
|
|
18
|
+
## Supported versions
|
|
19
|
+
|
|
20
|
+
Security fixes target the latest released version (`1.x`). Older pre-release snapshots are not maintained.
|
package/cordis.patch.yml
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
name: 'dsh-tower'
|
|
4
4
|
config:
|
|
5
5
|
allowAgentControl: true
|
|
6
|
+
- id: tower-skills
|
|
7
|
+
name: 'dsh-tower/skills'
|
|
6
8
|
- id: preset-tower-worker
|
|
7
9
|
name: '@deepseek-ai/dsh-agent-preset'
|
|
8
10
|
config:
|
|
@@ -21,3 +23,23 @@
|
|
|
21
23
|
sampleOverCapGlobResults: false
|
|
22
24
|
- id: tower-worker-pwsh
|
|
23
25
|
name: '@deepseek-ai/dsh-tool-pwsh'
|
|
26
|
+
- id: preset-tower-reviewer
|
|
27
|
+
name: '@deepseek-ai/dsh-agent-preset'
|
|
28
|
+
config:
|
|
29
|
+
id: tower-reviewer
|
|
30
|
+
name: Tower Reviewer
|
|
31
|
+
description: Read-only independent reviewer for a completed Tower mission.
|
|
32
|
+
order: 61
|
|
33
|
+
plugins:
|
|
34
|
+
- id: tower-reviewer-policy
|
|
35
|
+
name: 'dsh-tower/reviewer'
|
|
36
|
+
- id: tower-reviewer-fs
|
|
37
|
+
name: '@deepseek-ai/dsh-tool-fs'
|
|
38
|
+
config:
|
|
39
|
+
write: false
|
|
40
|
+
edit: false
|
|
41
|
+
readImage: false
|
|
42
|
+
- id: tower-reviewer-fs-search
|
|
43
|
+
name: '@deepseek-ai/dsh-tool-fs-search'
|
|
44
|
+
config:
|
|
45
|
+
sampleOverCapGlobResults: false
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# dsh-tower documentation
|
|
2
|
+
|
|
3
|
+
Choose a path:
|
|
4
|
+
|
|
5
|
+
| You are | Start here |
|
|
6
|
+
|---|---|
|
|
7
|
+
| New user | [Quick start](guide/quickstart.md) → [Tools](reference/tools.md) → [Example](examples/first-mission.md) |
|
|
8
|
+
| Operator | [Runbook](operations/runbook.md) → [Blockers](reference/blockers.md) → [Security](operations/security.md) |
|
|
9
|
+
| Maintainer | [Architecture](architecture/overview.md) → [Config](reference/config.md) → [Contributing](../CONTRIBUTING.md) |
|
|
10
|
+
|
|
11
|
+
Reference pages are the source of truth for the current release; the [tools reference](reference/tools.md) and [blocker list](reference/blockers.md) are checked by `npm run docs:check` in CI.
|
|
12
|
+
|
|
13
|
+
Other useful pages: [Install, upgrade and migration](guide/install-upgrade-migration.md), [FAQ](faq.md), [CHANGELOG](../CHANGELOG.md).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Architecture and state machine
|
|
2
|
+
|
|
3
|
+
## Components
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Host plugin (index.js)
|
|
7
|
+
├─ /tower command + Tower mode (guard, prompt section, tower_control)
|
|
8
|
+
├─ lib/tools.js -> 17 tools (+ tower_diff) registered on ctx.tools
|
|
9
|
+
└─ lib/tower.js -> Tower domain: missions, gates, worktrees, roster, review, merge
|
|
10
|
+
├─ lib/git.js minimal git port (execFile, bounded output, timeout classification)
|
|
11
|
+
├─ lib/store.js repo-scoped lock + atomic JSON + activity.jsonl
|
|
12
|
+
├─ lib/config.js Config schema + runtime snapshot
|
|
13
|
+
└─ lib/modeStore.js per-agent Tower mode persistence
|
|
14
|
+
Subagents
|
|
15
|
+
├─ tower-worker (dsh-tower/worker) read/write/edit/pwsh + mission tools
|
|
16
|
+
└─ tower-reviewer (dsh-tower/reviewer) read/glob/grep + tower_diff + review/finding
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The main checkout is the control-tower repo; each mission has a branch `tower/<id>-<slug>` and a
|
|
20
|
+
worktree in `.dsh-tower-worktrees/<hash>/<missionId>`. The plugin never operates on the main checkout's
|
|
21
|
+
working tree except merge and the read-only diff.
|
|
22
|
+
|
|
23
|
+
## Mission state machine
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
planned ──spawn worker──▶ active ──tower_complete──▶ completed ──tower_merge──▶ merged
|
|
27
|
+
│ │ ▲ │
|
|
28
|
+
│ │ └──── review round not clean ────┐
|
|
29
|
+
└── abandon ──▶ abandoned│ │
|
|
30
|
+
├── blocker ──▶ blocked ──active/completed──▶ ...
|
|
31
|
+
└── pause ─────▶ paused ──active──▶ ...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`merged` and `abandoned` are terminal. `tower_mission` may set `blocked → completed`; `merged` is only
|
|
35
|
+
set by `tower_merge`/reconciliation.
|
|
36
|
+
|
|
37
|
+
## Gate pipeline (`tower_preflight`)
|
|
38
|
+
|
|
39
|
+
1. Mission status must be `completed`; tasks and blockers are checked.
|
|
40
|
+
2. The worktree exists; the base tip is an ancestor of the branch tip; changed files are computed
|
|
41
|
+
against the merge base.
|
|
42
|
+
3. Survey missions must not change files; build missions must stay inside `scope`.
|
|
43
|
+
4. The main checkout must be clean for the files this merge would overwrite and on the base branch.
|
|
44
|
+
5. Dependency missions must be merged/abandoned.
|
|
45
|
+
6. A review must exist, belong to this mission, be clean, approve merge, and be bound to the tip
|
|
46
|
+
(unless a clean rebase waived it). No worker may be running.
|
|
47
|
+
|
|
48
|
+
## Merge
|
|
49
|
+
|
|
50
|
+
`tower_merge` runs `git merge --no-ff` with a configurable timeout (default 15 min) and 64 MB buffer.
|
|
51
|
+
After the command, regardless of exit status, it inspects `HEAD`:
|
|
52
|
+
|
|
53
|
+
- merge commit with parents `[baseTip, branchTip]` → record merged (including `recoveredFromFailure`);
|
|
54
|
+
- otherwise classify the failure: unmerged index → `merge-conflict` (mission blocked); no unmerged
|
|
55
|
+
entries → `merge-hook-failed` (mission retryable), with abort/reset cleanup.
|
|
56
|
+
- If the branch or its reviewed commit is already contained in the base, reconcile instead of merging.
|
|
57
|
+
|
|
58
|
+
## State files
|
|
59
|
+
|
|
60
|
+
| Path | Contents |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `<git-common-dir>/dsh-tower/state.json` | missions, roster, reviews, findings, messages, reviews sequence |
|
|
63
|
+
| `<git-common-dir>/dsh-tower/activity.jsonl` | append-only activity |
|
|
64
|
+
| `<git-common-dir>/dsh-tower/state.lock` | single-writer lock (stale locks are reported, never stolen) |
|
|
65
|
+
| `<checkout parent>/.dsh-tower-worktrees/<hash>/<missionId>` | mission worktree |
|
|
66
|
+
| `$DSH_HOME/tower-mode/modes.json` | Tower mode per agent/session |
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Example: first mission, end to end
|
|
2
|
+
|
|
3
|
+
Assume a repository at `D:\work\demo` with a clean `main` branch and one file `README.md`.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
/tower on
|
|
7
|
+
tower_init repoRoot=D:\work\demo
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
`s` is a placeholder for tool JSON arguments; DSH accepts the object form:
|
|
11
|
+
|
|
12
|
+
```json
|
|
13
|
+
{"repoRoot":"D:\\work\\demo","missions":[{"title":"Add greeting","scope":["src/**"],"tasks":["write src/greet.txt","commit the change"],"kind":"build"}]}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`tower_plan` returns `M1`. Spawn the worker:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{"repoRoot":"D:\\work\\demo","missionId":"M1","role":"worker"}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Wait for the worker's final report (delivered to this session) or check `tower_roster`. The worker
|
|
23
|
+
creates `src/greet.txt`, commits on `tower/m1-add-greeting`, marks both tasks done, and calls
|
|
24
|
+
`tower_complete`.
|
|
25
|
+
|
|
26
|
+
Spawn the reviewer:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{"repoRoot":"D:\\work\\demo","missionId":"M1","role":"reviewer"}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The read-only reviewer inspects `tower_diff`, then calls `tower_review` with:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{"repoRoot":"D:\\work\\demo","missionId":"M1","status":"clean","merge":"merge","findings":"none","decision":"single in-scope file; content correct"}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Gate and merge:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{"repoRoot":"D:\\work\\demo","missionId":"M1"}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Call `tower_preflight` with the same arguments; expect `ok: true`. Then `tower_merge`, and finally
|
|
45
|
+
`tower_teardown` with `{"repoRoot":"D:\\work\\demo"}`.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
/tower off
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Typical expected output after merge:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{"ok":true,"mergedCommit":"<sha>","baseTip":"<old>","branchTip":"<sha>","parents":["<old>","<sha>"]}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If the merge hook is slow, add `"timeoutMs": 1800000` to `tower_merge`; if a merge commit landed but the
|
|
58
|
+
call failed, re-run `tower_merge` and it records the existing commit instead of merging again.
|
package/docs/faq.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# FAQ
|
|
2
|
+
|
|
3
|
+
**Why did `tower_preflight` fail with `no-review` right after the worker finished?**
|
|
4
|
+
That is expected: a completed mission awaits review. Spawn a reviewer; after a clean review the gate
|
|
5
|
+
turns green.
|
|
6
|
+
|
|
7
|
+
**Do I need to poll the worker?**
|
|
8
|
+
No. The worker's final report is delivered to this session; `tower_roster` shows live activity. Polling
|
|
9
|
+
wastes turns.
|
|
10
|
+
|
|
11
|
+
**`/tower` says the mode is on but `tower_control` is missing.**
|
|
12
|
+
`mode.allowAgentControl` is off. Set it to `true` in the profile patch and restart DSH.
|
|
13
|
+
|
|
14
|
+
**The merge hook takes minutes and `tower_merge` failed.**
|
|
15
|
+
Retry with a larger `timeoutMs`: `tower_merge({ missionId, timeoutMs: 1800000 })`. The default is 15
|
|
16
|
+
minutes and merge output is buffered up to 64 MB. A hook failure returns `merge-hook-failed` and leaves
|
|
17
|
+
the repo clean for a retry.
|
|
18
|
+
|
|
19
|
+
**A merge commit exists but Tower says the mission is not merged.**
|
|
20
|
+
Call `tower_merge` again. It detects the landed merge commit (`reconciled`) or recovers a failed call
|
|
21
|
+
whose commit exists (`recoveredFromFailure`) and records it.
|
|
22
|
+
|
|
23
|
+
**`tower_rebase` returned `alreadyMerged` and changed nothing.**
|
|
24
|
+
Correct: the branch is already contained in the base. Call `tower_merge` to record the landed merge
|
|
25
|
+
instead of rebasing.
|
|
26
|
+
|
|
27
|
+
**Can a reviewer edit files?**
|
|
28
|
+
No. Reviewers use the read-only `tower-reviewer` preset (no write/edit/pwsh); a branch changed after the
|
|
29
|
+
reviewer spawned is rejected. Reviewers file `tower_finding` for issues outside the mission.
|
|
30
|
+
|
|
31
|
+
**Can workers use Tower tools to merge or spawn?**
|
|
32
|
+
No. The worker preset contains mission/progress tools only; merge and spawn live in the parent tower.
|
|
33
|
+
|
|
34
|
+
**Where does Tower store state?**
|
|
35
|
+
`<git-common-dir>/dsh-tower/state.json` and `activity.jsonl` in the target repo; mode state in
|
|
36
|
+
`$DSH_HOME/tower-mode/modes.json`. Worktrees live next to the checkout in `.dsh-tower-worktrees/`.
|
|
37
|
+
|
|
38
|
+
**How do I change the review round cap or presets?**
|
|
39
|
+
Use `review.maxRoundsPerReviewer` and `spawn.workerPreset`/`spawn.reviewerPreset`; see
|
|
40
|
+
[config](reference/config.md). Restart DSH after a profile config change.
|
|
41
|
+
|
|
42
|
+
**Does Tower send data anywhere?**
|
|
43
|
+
No. It has no telemetry and no network service; all state is local.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Install, upgrade and migration
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
- DSH `0.2.1-rc.2` or newer
|
|
6
|
+
- Node.js 22+
|
|
7
|
+
- Git (local worktrees; no network access is required)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
dsh plugin --profile web add dsh-tower
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or inside DSH: `plugin_manager({ action: "install_bundle", target: "dsh-tower" })`. From a checkout:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
dsh plugin --profile web add github:louifinn/dsh-tower
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The Host plugin loads at DSH startup: **restart DSH after install or update**.
|
|
22
|
+
|
|
23
|
+
## Upgrade
|
|
24
|
+
|
|
25
|
+
1. Read [CHANGELOG.md](../../CHANGELOG.md) for behavior changes.
|
|
26
|
+
2. Update the dependency (`dsh plugin --profile web add dsh-tower@latest` or your pinned version).
|
|
27
|
+
3. Restart DSH; check `tower_mode` reports `allowAgentControl` as expected.
|
|
28
|
+
4. Existing mission state stays compatible: the state schema did not change in 1.2/1.3.
|
|
29
|
+
|
|
30
|
+
## Migration notes
|
|
31
|
+
|
|
32
|
+
- **1.2 → 1.3**: reviewers moved from the shared `tower-worker` preset to the new read-only
|
|
33
|
+
`tower-reviewer` preset. Reinstall/restart so both preset rows exist. `tower_spawn role=reviewer`
|
|
34
|
+
now requires the mission to be `completed`.
|
|
35
|
+
- **1.1 → 1.2**: merge failures gained `merge-hook-failed`, `cleanup` and reconciliation behavior; no
|
|
36
|
+
state migration is needed.
|
|
37
|
+
- **1.0 → 1.1**: package name changed from `@local/dsh-tower` to `dsh-tower`. Remove the old bundle
|
|
38
|
+
(`remove_bundle` with `@local/dsh-tower`) and install `dsh-tower`.
|
|
39
|
+
|
|
40
|
+
## Uninstall
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
plugin_manager({ action: "remove_bundle", target: "dsh-tower" })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Restart DSH. Mission state under `<git-common-dir>/dsh-tower/` and worktrees under
|
|
47
|
+
`<checkout parent>/.dsh-tower-worktrees/` are left in place; remove them manually only after no mission
|
|
48
|
+
is active.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Quick start
|
|
2
|
+
|
|
3
|
+
Prerequisites: DSH `0.2.1-rc.2`+, Node.js 22+, Git. Install `dsh-tower` and restart DSH (see
|
|
4
|
+
[install](install-upgrade-migration.md)).
|
|
5
|
+
|
|
6
|
+
## 1. Enter Tower mode
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
/tower
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The session reports `Tower mode ON`. You are now the control tower; goal/todo/team delegation tools are
|
|
13
|
+
reserved for normal mode.
|
|
14
|
+
|
|
15
|
+
## 2. Initialize and plan
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
tower_init repoRoot=D:\work\repo
|
|
19
|
+
tower_plan repoRoot=D:\work\repo missions=[{"title":"Feature A","scope":["src/a/**"],"tasks":["implement the feature","commit the change"],"kind":"build"}]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`tower_plan` returns mission ids (`M1`). Each mission gets a branch and a worktree when spawned.
|
|
23
|
+
|
|
24
|
+
## 3. Run the worker
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
tower_spawn repoRoot=D:\work\repo missionId=M1 role=worker
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The call returns immediately with the child agent id. The worker works inside the mission worktree,
|
|
31
|
+
commits, marks tasks done, and calls `tower_complete`; its final report is delivered back to this
|
|
32
|
+
session. Use `tower_roster` to observe activity; do not poll in a loop.
|
|
33
|
+
|
|
34
|
+
## 4. Review
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
tower_spawn repoRoot=D:\work\repo missionId=M1 role=reviewer
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Reviewers use the read-only `tower-reviewer` preset. They inspect `tower_diff` and submit
|
|
41
|
+
`tower_review`. A `clean` + `merge` verdict is required to merge.
|
|
42
|
+
|
|
43
|
+
## 5. Gate, merge, clean up
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
tower_preflight repoRoot=D:\work\repo missionId=M1
|
|
47
|
+
tower_merge repoRoot=D:\work\repo missionId=M1
|
|
48
|
+
tower_teardown repoRoot=D:\work\repo
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`tower_preflight` before review is expected to report `no-review`; after a clean review it should be
|
|
52
|
+
`ok:true`. `tower_merge` refuses while blockers remain. `tower_teardown` removes merged/abandoned
|
|
53
|
+
worktrees and branches.
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
/tower off
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## If something fails
|
|
60
|
+
|
|
61
|
+
Read the blocker code and use [the blocker reference](reference/blockers.md). The most common cases:
|
|
62
|
+
`stale-base` (run `tower_rebase`), `no-review` (spawn a reviewer), `merge-hook-failed` (retry, or raise
|
|
63
|
+
`timeoutMs`), `tip-moved` (the branch changed after review; review again).
|
|
64
|
+
|
|
65
|
+
A complete copy-paste example: [examples/first-mission.md](../examples/first-mission.md).
|