dsh-any-agent 0.4.0 → 0.5.1
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 +80 -0
- package/DESIGN.md +9 -0
- package/README.md +49 -676
- package/README.zh.md +45 -690
- package/USAGE-NOTES.en.md +4 -3
- package/USAGE-NOTES.md +4 -3
- package/bin/dsh-any-agent.mjs +66 -2
- package/demo/mcp-client.mjs +130 -0
- package/demo/run.mjs +326 -0
- package/demo/transcript.json +47 -0
- package/docs/brand/README.md +58 -0
- package/docs/brand/header.jpg +0 -0
- package/docs/en/architecture.md +43 -0
- package/docs/en/compatibility.md +51 -0
- package/docs/en/demo.md +90 -0
- package/docs/en/install.md +257 -0
- package/docs/en/limits.md +27 -0
- package/docs/en/locking.md +24 -0
- package/docs/en/reference.md +42 -0
- package/docs/en/release.md +33 -0
- package/docs/en/security.md +66 -0
- package/docs/en/team.md +74 -0
- package/docs/en/tests.md +97 -0
- package/docs/en/usage.md +78 -0
- package/docs/zh/architecture.md +43 -0
- package/docs/zh/compatibility.md +49 -0
- package/docs/zh/demo.md +90 -0
- package/docs/zh/install.md +274 -0
- package/docs/zh/limits.md +30 -0
- package/docs/zh/locking.md +24 -0
- package/docs/zh/reference.md +42 -0
- package/docs/zh/release.md +38 -0
- package/docs/zh/security.md +66 -0
- package/docs/zh/team.md +71 -0
- package/docs/zh/tests.md +96 -0
- package/docs/zh/usage.md +76 -0
- package/package.json +5 -1
- package/scripts/check-release.mjs +73 -0
- package/src/config.mjs +90 -23
- package/src/host.mjs +1 -1
- package/src/hub.mjs +423 -69
- package/src/ids.mjs +21 -0
- package/src/mcp.mjs +15 -7
- package/src/redact.mjs +34 -0
- package/src/review.mjs +199 -0
- package/src/tools.mjs +184 -12
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,86 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.1] — 2026-11-04
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- **Path-safe ids before any state-file join.** `conversation_id` and `run_id`
|
|
15
|
+
are now validated as single path segments before building sentinel, review,
|
|
16
|
+
metadata or tombstone paths. Values containing `/`, `\`, `:`, `..` or other
|
|
17
|
+
separators are rejected instead of being passed to `path.join`; this closes
|
|
18
|
+
a traversal in `dsh_get`'s sentinel read-back and in `dsh_review`'s
|
|
19
|
+
`<state>/reviews/<conv>/<run>.review.json` write.
|
|
20
|
+
- **Filesystem-root `cwd` is rejected for writable tiers.** Choosing `/` or
|
|
21
|
+
`C:\` with `workspace-write` made the sandbox root the whole disk; symlinked
|
|
22
|
+
paths are resolved before the home/root guard, so a symlink to the root or
|
|
23
|
+
home cannot bypass it.
|
|
24
|
+
- **`title_hint` is capped at 200 characters** (schema + runtime). It is
|
|
25
|
+
persisted in the registry and sidecar on every turn; an unbounded value was
|
|
26
|
+
a stable disk/memory amplifier.
|
|
27
|
+
- **`dsh_doctor` checks `reviews/` writability**, alongside `runs/` and
|
|
28
|
+
`locks/`.
|
|
29
|
+
- **MCP tool dispatch only accepts own handler properties**, so names such as
|
|
30
|
+
`constructor`, `toString` or `__proto__` are reported as unknown tools
|
|
31
|
+
instead of reaching the prototype chain.
|
|
32
|
+
|
|
33
|
+
### Features
|
|
34
|
+
|
|
35
|
+
- **Caller review (`dsh_review`).** Every completed turn now carries a
|
|
36
|
+
`review` ticket in its result, `structuredContent`, `dsh_get` output and
|
|
37
|
+
async sentinel: a checklist derived from the turn (workspace changed? normal
|
|
38
|
+
stop? which tools ran?), the evidence needed to check it, and a `dsh_review`
|
|
39
|
+
entry. The calling agent can record `pass` / `partial` / `fail` with notes
|
|
40
|
+
and findings; the verdict is written atomically to
|
|
41
|
+
`<state dir>/reviews/<conversation_id>/<run_id>.review.json` and written back
|
|
42
|
+
into the in-memory run and the sentinel (so an async caller no longer sees a
|
|
43
|
+
stale `pending`). `fail`/`partial` require a reason; running/unknown runs are
|
|
44
|
+
refused. Default `advisory`: the ticket is loud but **does not block** the
|
|
45
|
+
next `dsh_send`, and a failed review is not fed back to DSH automatically.
|
|
46
|
+
Set `DSH_MCP_REVIEW=off` (or pass `review: "off"`) to disable tickets.
|
|
47
|
+
Review records share the sentinel's 7-day TTL and credential redaction,
|
|
48
|
+
including checklist items interpolated from `result.error` (Codex review
|
|
49
|
+
finding on #2).
|
|
50
|
+
|
|
51
|
+
## [0.5.0] — 2026-11-03
|
|
52
|
+
|
|
53
|
+
### Process model
|
|
54
|
+
|
|
55
|
+
- **The shared ACP process is now the default.** Sessions with the same `(cwd, permission)`
|
|
56
|
+
land on one DSH process: a second session costs ≈300 ms instead of a ≈4 s cold start, and the
|
|
57
|
+
300–480 MB is paid once per group instead of once per conversation.
|
|
58
|
+
`DSH_MCP_SHARED_PROCESS=0` restores one process per conversation (worth it when you need hard
|
|
59
|
+
isolation). Different workspaces or permission tiers still get separate processes —
|
|
60
|
+
`dsh-base` keeps the sandbox root and the approval policy process-wide.
|
|
61
|
+
- **`dsh_takeover` no longer kills a shared process blind.** When the PID it is about to kill
|
|
62
|
+
still carries other sessions, it refuses unless `force=true` and reports them as
|
|
63
|
+
`shared_sessions`; with `force` it says how many went down with it.
|
|
64
|
+
- **`dsh_doctor` de-duplicates RSS by PID** and reports the shared host count, so "live processes"
|
|
65
|
+
and "RSS total" stop multiplying by the number of sessions riding one process.
|
|
66
|
+
- Stale comments corrected: `DSH_MCP_IDLE_TTL_MS=0` is no longer the default (10 minutes since 0.4.0).
|
|
67
|
+
|
|
68
|
+
### Team mode and defaults
|
|
69
|
+
|
|
70
|
+
- **Agent Teams is now opt-in.** `DSH_MCP_TEAM` defaults to off. Enable with
|
|
71
|
+
`dsh-any-agent team on` (writes `<DSH_HOME>/.dsh-any-agent/settings.json`),
|
|
72
|
+
disable with `team off`, inspect with `team status`; no client config editing.
|
|
73
|
+
When on, the profile switches to `dsh-team`, the legacy subagent tools are
|
|
74
|
+
disabled, and `dsh_send` adds the "form a team when needed" line.
|
|
75
|
+
- **The default session permission is now `workspace-write`** (was
|
|
76
|
+
`danger-full-access`). `permission=danger-full-access` additionally requires
|
|
77
|
+
`DSH_MCP_ALLOW_FULL_ACCESS=1` on the machine, so a bare `dsh_start` can no
|
|
78
|
+
longer hand the caller whole-machine write access.
|
|
79
|
+
- `dsh_doctor` has a dedicated `team` check: on / off / requested-but-unavailable.
|
|
80
|
+
|
|
81
|
+
### Demo
|
|
82
|
+
|
|
83
|
+
- **`npm run demo`** — a five-minute, end-to-end case: create a conversation, dispatch a task with
|
|
84
|
+
`wait=false`, interject twice while it runs, collect the sentinel, then verify the artifacts on
|
|
85
|
+
disk (including `node --test`) instead of trusting the model's own summary. It runs against an
|
|
86
|
+
isolated registry / sentinel / lock directory, so it never touches your daily
|
|
87
|
+
`~/.dsh/.dsh-any-agent`. `--replay` walks a recorded transcript with no DSH and no tokens;
|
|
88
|
+
`--keep` keeps the workspace. Ships as `demo/` in the npm package.
|
|
89
|
+
|
|
10
90
|
## [0.4.0] — 2026-11-03
|
|
11
91
|
|
|
12
92
|
### Memory
|
package/DESIGN.md
CHANGED
|
@@ -238,6 +238,7 @@ Profile = `~/.dsh/profiles/<name>/`,由 `package.json` 的 `dsh.profile.bundle
|
|
|
238
238
|
| B8 | 上下文注入 | `contentBlocks` 支持文本与图片;支持 DSH 的 `@路径` 引用语法把文件带进会话 |
|
|
239
239
|
| B9 | 异步完成哨兵 | `wait=false` 的 run 终止(done/error/cancelled)时,往 `<RUNS_DIR>/<conversation_id>/<run_id>.json` 原子写出含 `status`+`result` 的文件;调用方(Claude/Codex)用后台任务 `until [ -f ]` 等它出现即被唤醒,无需轮询、也不占轮次 |
|
|
240
240
|
| B10 | 写锁抢占 | 写锁是跨进程内核信号量(持有者活着不过期、**无夺锁 API**),所以"抢"= 杀掉持有者。本服务登记自己拉起的子进程(`<状态目录>/locks/<会话id>.json`:pid + token + 心跳),据此把持有者分成四类,只对**判定为本服务残留的孤儿**自动动手;无法识别的一律不杀(见下) |
|
|
241
|
+
| B11 | 调用方审核 | 每个完成的回合在 `result.review` 带一张审核单(核验清单 + 证据摘要 + `dsh_review` 入口);调用方独立核验后提交 `pass`/`partial`/`fail`,裁决落盘 `<状态目录>/reviews/<会话id>/<run_id>.review.json`,并回写内存 run 与哨兵。默认 advisory:**不阻塞**后续派活,也不自动回灌给 DSH |
|
|
241
242
|
|
|
242
243
|
**B9 设计要点(并发正确性)**:本服务在 MCP 协议上只应答请求、**不主动推通知**,"跑完通知"由哨兵文件 + 调用方后台等待任务共同实现。
|
|
243
244
|
- **按会话分目录**:`run_id` 序号是每会话独立计数的,不同会话可能同毫秒生成同名 `run_id`;`<conversation_id>/` 前缀使其各写各的、不串台。会话内 `dsh_send` 忙时直接拒绝、序号单调,故会话内唯一。
|
|
@@ -249,6 +250,14 @@ Profile = `~/.dsh/profiles/<name>/`,由 `package.json` 的 `dsh.profile.bundle
|
|
|
249
250
|
- **给调用方的便利**:返回值同时给出 `sentinel_file_posix`(`D:\a\b` → `/d/a/b`)。手工转换 Windows 路径是等待任务白等到超时最常见的原因。
|
|
250
251
|
- **落点**:`tools.dsh_send` 的 `wait=false` 分支在 `.then`/`.catch` 各调一次 `hub.writeRunSentinel()`(覆盖所有终止态);`createRun` 起跑前 `clearRunSentinel()` 防御历史残留。
|
|
251
252
|
|
|
253
|
+
**B11 设计要点(调用方审核)**
|
|
254
|
+
- **为什么放进返回载荷**:返回值是"另一个 agent 的自述"。调用方最常见的失败模式是直接转述"已完成/已修好"。审核要求必须和结果出现在同一条消息里(`content.text`、`structuredContent`、哨兵 `result.review`),才可能改变调用方的下一步动作;单独一个"记得审核"的约定会被忽略。
|
|
255
|
+
- **审核单形状**:`{review_id, mode, status, run_id, prompt_preview, checklist, evidence, submit:{tool:"dsh_review", arguments}, instructions}`。清单随回合形状变化(有没有改工作区、有没有错误、用了哪些工具);`evidence` 只放核对所需的最小事实(stop_reason、error、changed_files、工具名)。
|
|
256
|
+
- **默认 advisory,不阻塞**:这是刻意的产品选择。强制门禁会打断"审核失败 → 派修复回合"的自然流程,也会破坏既有调用方;advisory 只把审核请求显性化。`review=off` 可按会话/单次派活关闭;即使 off,`dsh_review` 仍可对旧 run 显式补审核。
|
|
257
|
+
- **裁决落盘与回写**:`reviews/<会话id>/<run_id>.review.json` 原子写入(tmp + rename,唯一 tmp 名),含裁决、理由/findings、清单与证据。`submitReview` 依次从内存 run → 哨兵 → 已有审核记录取事实,所以服务重启、run 被挤出内存后仍可审核。提交后 **原子重写哨兵里的 `result.review`**,避免异步调用方下次读文件看到与事实相反的 `pending`。
|
|
258
|
+
- **隐私**:审核单不含思考正文;理由、findings、任务预览、证据**与每个 checklist 项**在落盘前都走 `redact.sanitizeApprovalContext`(与审批上下文同一套规则)——checklist 会拼接 `result.error`,漏掉它就会成为第二个明文泄漏点。审核记录与哨兵共用 `DSH_MCP_SENTINEL_TTL_MS` 的 7 天清理。
|
|
259
|
+
- **失败语义**:fail/partial 必须有 notes 或 findings;对 `running`/`queued` 的 run 提交会被拒绝;写盘失败直接抛错(审核本身就是这次操作,静默失败比报错更糟)。
|
|
260
|
+
|
|
252
261
|
**B10 设计要点(写锁与抢占)**
|
|
253
262
|
- **为什么需要**:DSH 的 `SessionWriteLease` 是 Windows 命名内核信号量,**持有者活着就永不过期**,且**没有 API 能从活进程手里夺走** —— 唯一手段是杀掉持有者进程。既然要杀,就必须先判定那是谁。
|
|
254
263
|
- **登记**:一个会话一个文件 `<状态目录>/locks/<会话id>.json`,内容 `{mcp_pid, mcp_token, child_pid, cwd, spawned_at, heartbeat_at}`。`mcp_token` 防 PID 复用;`heartbeat_at` 由 reaper 周期刷新。
|