dsh-as-mcp 0.54.0 → 0.55.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 +46 -0
- package/CHANGELOG.zh.md +7 -0
- package/README.md +38 -7
- package/README.zh.md +33 -6
- package/cordis.patch.yml +4 -3
- package/lib/index.d.ts +125 -0
- package/lib/index.js +294 -1
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,52 @@ plugin is pre-1.0, so a minor bump is a feature and a patch is a fix or docs.
|
|
|
6
6
|
A Chinese summary of every release lives in [CHANGELOG.zh.md](./CHANGELOG.zh.md);
|
|
7
7
|
this file stays the authoritative record.
|
|
8
8
|
|
|
9
|
+
## 0.55.0
|
|
10
|
+
|
|
11
|
+
- Three new tools in the `session` group — `session_command_list`, `session_command` and
|
|
12
|
+
`session_goal` — bring the surface from 35 to 38 (the group from 18 to 21). A slash command
|
|
13
|
+
executes **without a model turn** (the registry parses the line and invokes the handler directly),
|
|
14
|
+
so `session_command` is the cheap half of "do what a human typing into the DSH composer would
|
|
15
|
+
do": compact a long session, export its log, enter plan mode or set a goal. `session_command_list`
|
|
16
|
+
reports every command the session's agent can see, with its description and input hint, and marks
|
|
17
|
+
each `permitted` instead of hiding the refused ones; `session_command` takes the line with or
|
|
18
|
+
without its leading slash and with arguments in the same string (`"compact"`, `"/export"`,
|
|
19
|
+
`"/goal <objective>"`, `"/plan off"`), and the run goes through the harness's own command
|
|
20
|
+
registry, so it is the same execution a human typing the line produces (the reply carries the
|
|
21
|
+
harness's `commandId`, `ok`, the handler's `text` and the `sourceEventSeq` it recorded). The
|
|
22
|
+
admitted set is explicit — `/compact`, `/plan`, `/goal`, `/export` — not "anything the registry
|
|
23
|
+
lists": `/permission` would change the session's permission preset (a remote caller must not
|
|
24
|
+
escalate its own session) and `/feedback` writes the user's own review records, so both are
|
|
25
|
+
refused **before** any handler runs — a refused call writes nothing to the session log — and an
|
|
26
|
+
unknown name is refused with the admitted set listed. A handler that reports an error comes back
|
|
27
|
+
as `ok: false` with its text, while a name/argument the registry did not admit is an error saying
|
|
28
|
+
nothing was logged.
|
|
29
|
+
- `session_goal` reads and mutates the session's **durable goal** — the long-running objective the
|
|
30
|
+
harness's round driver keeps advancing across turns, which is how a caller delegates multi-hour
|
|
31
|
+
work instead of holding one turn open. `action` is `get` (the goal, or `null`) or
|
|
32
|
+
`create`/`edit`/`pause`/`resume`/`complete`/`clear`; `create` needs `objective`, `edit` needs
|
|
33
|
+
`objective` and/or `maxGoalRounds`, and every other action acts on the goal that exists now, so
|
|
34
|
+
its ref comes from a read rather than from the caller. `expectedRevision` (from a previous read) is
|
|
35
|
+
the lost-update guard: a mutation whose goal changed underneath — say the user edited it in the DSH
|
|
36
|
+
UI a moment ago — is refused instead of silently applied, the same spirit as the UI's conflict
|
|
37
|
+
handling. A goal reports `id`, `revision`, `objective`, `phase` (`active`/`paused`/`blocked`/
|
|
38
|
+
`complete`), `activation` (`armed`/`disarmed`), `maxGoalRounds`, `roundsStarted`, `blockedReason`
|
|
39
|
+
when blocked and its timestamps; `clear` returns `goal: null` with `cleared: true`, and `complete`
|
|
40
|
+
returns the completed goal.
|
|
41
|
+
- `dsh_info.harnessServices` reports two more probes, `commands` and `goals` (29 in total, up from
|
|
42
|
+
27), and `toolAvailability` pre-flights the three tools against `sessionController` +
|
|
43
|
+
`sessionController.resolveAgent` plus the matching service, so an agent-scoped command or goal
|
|
44
|
+
service that a profile does not mount is reported unavailable instead of failing at call time.
|
|
45
|
+
`dsh_info.notes` gains one paragraph per surface: commands run without a model turn and only the
|
|
46
|
+
user-plane set is admitted, and goal mutations carry the harness's revision guard.
|
|
47
|
+
- No new configuration keys: push, the two transports, the tool groups and the settings panel are
|
|
48
|
+
unchanged. Both READMEs gain the three tool rows, a "Session orchestration" flow and a
|
|
49
|
+
deployment-requirements row; `docs/CAPABILITY-GAPS.md` is re-counted to 38 tools and 29 probes and
|
|
50
|
+
marks slash commands and durable goals as shipped, with `resolveAgent` named as the bridging layer.
|
|
51
|
+
`tests/orchestration.test.ts` pins the new driver surface (allow-list before the handler, slash
|
|
52
|
+
normalization, the revision guard, projection of the goal view) and `tests/mcp-endpoint.test.ts`
|
|
53
|
+
the argument passthrough; `tests/availability.test.ts` covers the new pre-flight rules.
|
|
54
|
+
|
|
9
55
|
## 0.54.0
|
|
10
56
|
|
|
11
57
|
- Server push now covers the whole run instead of one turn-ending signal. The existing
|
package/CHANGELOG.zh.md
CHANGED
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
- 两份文件的**版本标题必须完全一致**(集合与顺序),由 `tools/check-docs.mjs` 强制检查——发布时漏写中文条目会让检查失败,中文读者不会悄悄落后一个版本。
|
|
7
7
|
- 版本号遵循 SemVer;项目仍是 pre-1.0,所以 minor 是功能,patch 是修复或文档。
|
|
8
8
|
|
|
9
|
+
## 0.55.0
|
|
10
|
+
|
|
11
|
+
- 新增 3 个工具(`session_command_list`、`session_command`、`session_goal`),工具面从 35 增至 38,仍全在 session 组(该组从 18 增至 21)。斜杠命令**不消耗模型轮次**(注册表解析命令行后直接调用 handler),所以 `session_command` 就是「替人在 DSH 输入框里敲命令」的便宜一半:压缩长会话、导出日志、进入 plan 模式、设置目标。`session_command_list` 报出该会话 agent 能看到的全部命令(描述与输入提示),并用 `permitted` 标出本端点会执行哪些,而不是把被拒绝的隐藏掉;`session_command` 接受带或不带前导斜杠的命令行、参数写在同一字符串里(`"compact"`、`"/export"`、`"/goal <objective>"`、`"/plan off"`),执行走 harness 自己的命令注册表,与真人敲同一行的执行完全一致(回复带 harness 的 `commandId`、`ok`、handler 的 `text`,以及它记录的 `sourceEventSeq`)。放行集是显式的 `/compact`、`/plan`、`/goal`、`/export`,而不是「注册表里有什么就运行什么」:`/permission` 会改变会话的权限 preset(远程调用方不能给自己提权),`/feedback` 会写入用户自己的评审记录,两者在**任何 handler 运行之前**就被拒绝——被拒调用不向会话日志写任何东西;未注册的名字会被拒绝并列出放行集。handler 报错时返回 `ok: false` 与文本,注册表未接收的名字/参数返回「什么都没写进日志」的错误。
|
|
12
|
+
- `session_goal` 读取和修改会话的**持久目标**——harness 的轮次驱动会在多轮之间持续推进的长期目标,调用方因此可以委派多小时的任务,而不必一直挂着一轮。`action` 取 `get`(返回目标或 `null`)或 `create`/`edit`/`pause`/`resume`/`complete`/`clear`;`create` 需要 `objective`,`edit` 需要 `objective` 和/或 `maxGoalRounds`,其余动作作用于当前存在的目标,其 ref 来自一次读取而不是调用方。`expectedRevision`(来自上一次读取)是丢失更新保护:目标在脚下被改过(比如用户刚在 DSH 界面里编辑过)就拒绝,而不是悄悄覆盖——与界面冲突处理同一思路。目标报出 `id`、`revision`、`objective`、`phase`(`active`/`paused`/`blocked`/`complete`)、`activation`(`armed`/`disarmed`)、`maxGoalRounds`、`roundsStarted`、被阻塞时的 `blockedReason` 与时间戳;`clear` 返回 `goal: null`(`cleared: true`),`complete` 返回已完成的目标。
|
|
13
|
+
- `dsh_info.harnessServices` 新增 `commands` 与 `goals` 两个探针(共 29 个,原为 27 个);`dsh_info.toolAvailability` 对三个新工具做预检,要求 `sessionController` + `sessionController.resolveAgent` 以及对应的 commands/goals 服务,缺能力的 profile 会把它们报成不可用,而不是等到调用时才失败;`dsh_info.notes` 各增一段(命令不消耗轮次且只放行用户面集合;目标修改带 harness 的 revision 保护)。
|
|
14
|
+
- 无新增配置键:推送、两种传输、工具组与设置面板均不变。两份 README 增加三行工具表、「会话编排 / Session orchestration」流程与部署要求行;`docs/CAPABILITY-GAPS.md` 的工具数/探针数更新为 38/29,并把斜杠命令与持久目标两条差距标记为已交付、点名 `resolveAgent` 是打通它们的桥。`tests/orchestration.test.ts` 钉住新驱动面(handler 之前的 allow-list、斜杠归一化、revision 保护、目标投影),`tests/mcp-endpoint.test.ts` 钉住参数透传,`tests/availability.test.ts` 覆盖新的预检规则。
|
|
15
|
+
|
|
9
16
|
## 0.54.0
|
|
10
17
|
|
|
11
18
|
- 服务端推送从「只报一轮结束」扩展到整场运行,仍走现有的 `subscriptions/listen` 流,且不新增任何工具(工具面仍是 35 个):`dsh://sessions`(会话增删、活动/状态、subagent 起止)、`dsh://session/<id>/events`(最新持久事件投影为 `{seq, type, at, turn, step, summary, error}`,每次追加都发布,单次读取至多 64 条)、`dsh://session/<id>/assistant`(实时 `start`/`text`/`reasoning`/`tool`/`finish`/`end` 帧,`seq` 每会话单调递增)、`dsh://session/<id>/status`(与 `session_status` 同负载)、`dsh://session/<id>/projections`(白名单内的 `workingDirectory`/`worktree`/`goal`/`plan`/`todos`/`agentPreset`/`title`)、`dsh://jobs` 与 `dsh://jobs/<id>`(后台 shell 任务;任务尾部读取不消费,不会抢走 `shell_output` 的字节)。0.52.86 的 `dsh://session/<id>/turn` 结算资源保留,成为整个 feed 集合中的一项。`resources/list`/`resources/templates/list` 会公布这些 feed,`dsh_info.notes` 也会点名它们。
|
package/README.md
CHANGED
|
@@ -252,6 +252,9 @@ schema-aware client can read the object directly instead of parsing JSON out of
|
|
|
252
252
|
| `session_unarchive` | Return an archived session to its recorded position in the DSH UI. Idempotent: an id that is not archived is not an error, and the log was never touched, so the session is visible and promptable again. |
|
|
253
253
|
| `session_pin` | Pin a session to the top of the DSH UI's session list (the same pin set the sidebar writes). The session must exist and must not be archived; pinning is idempotent. |
|
|
254
254
|
| `session_unpin` | Remove a session from the DSH UI's pin set. Idempotent: an id that is not pinned is not an error. |
|
|
255
|
+
| `session_command_list` | The slash commands this session's agent can run — each with a one-line description, an optional `input` hint and a `permitted` flag — so a caller can see that a command exists while this endpoint deliberately will not run it (the admitted set is `/compact`, `/plan`, `/goal`, `/export`; `/permission` and `/feedback` are refused on purpose). A command executes without a model turn — the handler is invoked directly — which makes this the cheap way to compact a long session, export its log, enter plan mode or set a goal, exactly as a human typing into the DSH composer would. Read-only; run one with `session_command`. |
|
|
256
|
+
| `session_command` | Execute one slash command against a session's agent without spending a model turn, as if the human had typed it into the DSH composer: give the `command` with or without its leading slash, with its arguments in the same string (`"compact"`, `"/export"`, `"/goal <objective>"`, `"/plan off"`). The run goes through the harness's own command registry, so it is the same execution a human typing the line produces and the session log records it; the reply carries the harness's `commandId`, `ok` (`false` when the handler itself reported an error result), the handler's `text` and the `sourceEventSeq` it recorded when it reported one. Only the user-plane commands are admitted (`/compact`, `/plan`, `/goal`, `/export`); `/permission` (which would change the session permission preset) and `/feedback` (which would write the user's own review records) are refused before any handler runs, so a refused call writes nothing to the session log, and a name that is not registered on this profile is refused with the admitted set listed. A name or argument the registry did not admit is an error saying nothing was logged. Call `session_command_list` first to see the exact names and the input hint this agent accepts. |
|
|
257
|
+
| `session_goal` | Read or mutate the session's durable goal — the long-running objective the harness's round driver keeps advancing across turns, which is how a caller delegates multi-hour work instead of holding one turn open. `action` is `get` (which returns the `goal` or `null`), or one of `create` (needs `objective`), `edit` (needs `objective` and/or `maxGoalRounds`), `pause`, `resume`, `complete`, `clear`; every action except `create` acts on the goal that exists now, so its ref comes from a read rather than from the caller. Pass `expectedRevision` (from a previous read) to refuse a mutation whose goal changed underneath you — the equivalent of the DSH UI's conflict handling. A goal reports `id`, `revision`, `objective`, `phase` (`active`/`paused`/`blocked`/`complete`), `activation` (`armed`/`disarmed`), `maxGoalRounds`, `roundsStarted`, `blockedReason` when blocked, and `createdAt`/`updatedAt`; `clear` returns `goal: null` (and `cleared: true`), while `complete` returns the completed goal. |
|
|
255
258
|
| `session_usage` | Token usage per session (its subagents included by default): exact bucket counts, a per-model breakdown, optional per-turn detail, and money when a price plugin is installed. Pass `sessionIds` to tally several known sessions in one receipt, or `cwd` to total every session that ran in one workspace directory. |
|
|
256
259
|
| `file_read` | Read a UTF-8 file through DSH's filesystem service. Bounded by a byte limit; `offset`/`nextOffset` page through a larger file (both are **byte** offsets, so an offset at or past EOF is an empty complete read), `maxBytes` asks for a smaller window so reading a slice costs less, and `sha256: true` returns the whole file's hash as the compare-and-swap value for a later write/edit. |
|
|
257
260
|
| `file_read_image` | Read an image (png/jpeg/gif/webp/bmp, detected from the bytes) and return it as an MCP image block, preceded by a metadata block (path, mimeType, bytes), so you can look at a screenshot or chart instead of guessing. Capped at 8 MiB. |
|
|
@@ -311,6 +314,33 @@ refused *and* hidden from `dsh_tool_list`. The omissions that matter:
|
|
|
311
314
|
An operator can widen this deliberately with the `agentTools.allow` setting, or subtract from
|
|
312
315
|
the default with `agentTools.deny`.
|
|
313
316
|
|
|
317
|
+
### Session orchestration
|
|
318
|
+
|
|
319
|
+
Some work is not a single prompt. Compacting a long session, exporting its log, entering plan mode or
|
|
320
|
+
setting a goal are user-plane actions a human performs by typing a slash command into the DSH
|
|
321
|
+
composer; a durable goal also turns a one-turn request into an objective the harness's round driver
|
|
322
|
+
keeps advancing across turns. Neither path costs a model turn of its own. The intended flow:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
session_command_list { sessionId } # what this agent accepts; permitted marks the admitted set
|
|
326
|
+
-> session_command { sessionId, command: "/compact" } # run one; no model turn
|
|
327
|
+
-> session_goal { sessionId, action: "create", objective: "Finish the migration", maxGoalRounds: 20 }
|
|
328
|
+
-> session_goal { sessionId, action: "get" } # poll phase/revision...
|
|
329
|
+
or subscribe to dsh://session/<id>/projections # ...or be told when the goal changes
|
|
330
|
+
-> session_goal { sessionId, action: "pause", expectedRevision: 3 } # then resume / complete / clear
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
`session_command_list` reports each entry's `permitted` flag instead of hiding the refused commands, so
|
|
334
|
+
a caller can see that `/permission` and `/feedback` exist on the profile and that this endpoint
|
|
335
|
+
deliberately will not run them: `/permission` would change the session's permission preset and
|
|
336
|
+
`/feedback` would write the user's own review records, and neither reaches a handler or writes anything
|
|
337
|
+
to the session log. `session_status` reports the session's turn and log state, not the goal itself; the
|
|
338
|
+
goal's phase and revision come from `session_goal { action: "get" }`, and the
|
|
339
|
+
`dsh://session/<id>/projections` feed (which includes `goal`) publishes when it changes, so following a
|
|
340
|
+
goal is a poll or a subscription rather than a prompt per turn. A mutation without `expectedRevision`
|
|
341
|
+
acts on the goal that exists at that moment; pass the revision a read returned when two supervisors
|
|
342
|
+
could race.
|
|
343
|
+
|
|
314
344
|
## Worktrees
|
|
315
345
|
|
|
316
346
|
A session can work in its own Git checkout of the same repository. The operation is the one the
|
|
@@ -361,7 +391,7 @@ An MCP server cannot call a client on its own, so every "something changed" trav
|
|
|
361
391
|
resource notification. This endpoint publishes one change-feed surface over both standard push
|
|
362
392
|
channels, and each notification is **payload-less**: it names a synthetic resource, and the
|
|
363
393
|
client reads that resource for the JSON. No tools were added for this — the surface is still the
|
|
364
|
-
|
|
394
|
+
38 in the table above — and push has no configuration key: it is always on and costs nothing
|
|
365
395
|
while nobody subscribes.
|
|
366
396
|
|
|
367
397
|
| Client era | Handshake | Server→client channel | How push is requested |
|
|
@@ -1007,6 +1037,7 @@ but a deployment can switch the capability off.
|
|
|
1007
1037
|
| `worktree_create` / `worktree_restore` / `session_create { worktree }` | `worktrees` **and** `sessionController.resolveAgent` | the forge bundle in DSH Desktop/Web provides the service (`@deepseek-ai/dsh-experimental-forge-bundle`); a bare profile has none, `dsh_info.toolAvailability` reports the tools unavailable, and `session_create { worktree }` is refused before the session exists. Git and Node must exist in the session's execution world |
|
|
1008
1038
|
| `session_status.workingDirectory` / `session_status.worktree` / `session_list { includeWorktree }` | readable session projections (`sessionController.projections` or `sessionQuery.observeSession`) | optional: without them both replies omit those two fields and the rest is unchanged |
|
|
1009
1039
|
| `session_archive` / `session_unarchive` / `session_pin` / `session_unpin` | `workspaceRegistry.archiveSession` / `pinSession` | the same registry sets the DSH sidebar's actions write; if the methods are missing the tools are reported unavailable |
|
|
1040
|
+
| `session_command_list` / `session_command` / `session_goal` | `sessionController.resolveAgent` **and** `commands` / `goals` | both services are agent-scoped, so the session's live agent is the bridge; a profile without them reports the three tools unavailable in `dsh_info.toolAvailability`, and each call names the missing service and the bundle that provides it |
|
|
1010
1041
|
| `workspace_*` | `workspaceRegistry` | provided by the base bundle in normal profiles |
|
|
1011
1042
|
| `file_*` | `fs` | the deployment's sandbox policy still applies; `file_read_image` additionally needs a supported image format |
|
|
1012
1043
|
| `shell_*` | `shell` | `shell_run` uses `run()` when present and `execute()` otherwise; background jobs need `start()` or `execute()` |
|
|
@@ -1180,14 +1211,14 @@ build:
|
|
|
1180
1211
|
|
|
1181
1212
|
| Tool groups enabled | Tools advertised | Last result |
|
|
1182
1213
|
| --- | --- | --- |
|
|
1183
|
-
| all six |
|
|
1184
|
-
| `session`, `usage` only |
|
|
1185
|
-
| `workspace`, `session`, `agentTools`, `usage` |
|
|
1214
|
+
| all six | 38 | live-checks 195/195 with `--with-model --with-subagent` and acceptance 166/166 — one boot, search index open |
|
|
1215
|
+
| `session`, `usage` only | 23 | live-checks 95/95 with `--with-model` (file/shell/workspace sections skip) |
|
|
1216
|
+
| `workspace`, `session`, `agentTools`, `usage` | 28 | live-checks 64/64 (a disabled group's reason names its `tools.<group>` switch) |
|
|
1186
1217
|
| `files`, `shell` only | 11 | live-checks 68/68 (session/usage/queue/search sections skip) |
|
|
1187
1218
|
|
|
1188
|
-
The advertised counts are the current surface (0.
|
|
1189
|
-
last-result column records runs made against 0.52.86, before the
|
|
1190
|
-
re-measured
|
|
1219
|
+
The advertised counts are the current surface (0.55.0: 21 session-group tools and 38 overall); the
|
|
1220
|
+
last-result column records runs made against 0.52.86, before the 0.53.0 additions, and has not been
|
|
1221
|
+
re-measured since.
|
|
1191
1222
|
|
|
1192
1223
|
A fifth shape, `mountonly`, boots with `http.enabled: false` and `mountOnWebServer: true` and
|
|
1193
1224
|
checks the endpoint over DSH's own web server: 109/109, all cheap, including the assertions that
|
package/README.zh.md
CHANGED
|
@@ -237,6 +237,9 @@ scheme 可以省略:`127.0.0.1:8790/mcp` 也能识别。
|
|
|
237
237
|
| `session_unarchive` | 把归档的会话放回它在 DSH 界面里记录的位置。幂等:对未归档的 id 不算错误;日志从未被动过,因此会话重新可见、可继续 prompt。 |
|
|
238
238
|
| `session_pin` | 把会话置顶到 DSH 界面的会话列表(与侧边栏置顶写入同一个 pin 集合)。会话必须存在且未归档;置顶幂等。 |
|
|
239
239
|
| `session_unpin` | 把会话从 DSH 界面的 pin 集合里移除。幂等:对未置顶的 id 不算错误。 |
|
|
240
|
+
| `session_command_list` | 列出该会话 agent 能运行的斜杠命令——每条带一句描述、可选的 `input` 提示与 `permitted` 标记——因此调用方能看到某个命令存在、而本端点故意不运行它(放行集是 `/compact`、`/plan`、`/goal`、`/export`;`/permission` 与 `/feedback` 刻意拒绝)。命令**不消耗模型轮次**(直接调用 handler),所以这是压缩长会话、导出日志、进入 plan 模式或设置目标最便宜的办法——与人在 DSH 输入框里敲命令完全一致。只读;用 `session_command` 运行其中一条。 |
|
|
241
|
+
| `session_command` | 对会话的 agent 执行一条斜杠命令,不花模型轮次,就像人真的在 DSH 输入框里敲了它:`command` 可带可不带前导斜杠,参数写在同一字符串里(`"compact"`、`"/export"`、`"/goal <objective>"`、`"/plan off"`)。执行走 harness 自己的命令注册表,因此与真人敲同一行的执行完全一致,会话日志也会记录;回复带 harness 的 `commandId`、`ok`(handler 自己报告错误结果时为 `false`)、handler 的 `text`,以及它报告时记录的 `sourceEventSeq`。只放行用户面命令(`/compact`、`/plan`、`/goal`、`/export`);`/permission`(会改变会话的 permission preset)与 `/feedback`(会写入你自己的评审记录)在任何 handler 运行之前就被拒绝,因此被拒调用**不会向会话日志写入任何东西**;本 profile 未注册的命令名会被拒绝并列出放行集。注册表没有接收的名字或参数返回错误并说明什么都没写进日志。先调 `session_command_list` 看该 agent 接受的确切名字与输入提示。 |
|
|
242
|
+
| `session_goal` | 读取或修改会话的持久目标(durable goal)——即 harness 的轮次驱动会在多轮之间持续推进的长期目标,调用方因此可以委派多小时的任务,而不必一直挂着一轮。`action` 取 `get`(返回 `goal` 或 `null`),或 `create`(需要 `objective`)、`edit`(需要 `objective` 和/或 `maxGoalRounds`)、`pause`、`resume`、`complete`、`clear` 之一;除 `create` 外的动作都作用于当前存在的目标,其 ref 来自一次读取而不是调用方。传 `expectedRevision`(来自之前的读取)可拒绝"目标已在脚下被改过"的修改——相当于 DSH 界面的冲突处理。目标报出 `id`、`revision`、`objective`、`phase`(`active`/`paused`/`blocked`/`complete`)、`activation`(`armed`/`disarmed`)、`maxGoalRounds`、`roundsStarted`、被阻塞时的 `blockedReason` 与 `createdAt`/`updatedAt`;`clear` 返回 `goal: null`(并带 `cleared: true`),`complete` 返回已完成的目标。 |
|
|
240
243
|
| `session_usage` | 按会话统计 token 用量(默认含其 subagent):精确的四桶计数、按模型拆分、可选的按轮拆分,装了价格插件时还有钱。传 `sessionIds` 可一次结算多个已知会话,传 `cwd` 可汇总某个工作目录里跑过的所有会话。 |
|
|
241
244
|
| `file_read` | 经 DSH 文件系统服务读 UTF-8 文件。受字节上限约束;`offset`/`nextOffset` 可翻页读取更大的文件(两者都是**字节**偏移,因此 EOF 处或之后的偏移是空的完整读取),`maxBytes` 用更小的窗口降低单次开销;`sha256: true` 返回整个文件的哈希,作为后续写入/编辑的比较并交换值。 |
|
|
242
245
|
| `file_read_image` | 读取图片(png/jpeg/gif/webp/bmp,按字节嗅探)并以 MCP image block 返回,让你直接看截图或图表而不是靠猜。上限 8 MiB。 |
|
|
@@ -288,6 +291,29 @@ workspace_create { path: "/Users/me/project" }
|
|
|
288
291
|
|
|
289
292
|
运维方可以通过 `agentTools.allow` 有意放宽,或用 `agentTools.deny` 从默认集合里减去。
|
|
290
293
|
|
|
294
|
+
### 会话编排
|
|
295
|
+
|
|
296
|
+
有些活不是一次 prompt 能装下的。压缩长会话、导出日志、进入 plan 模式、设置目标,都是人在 DSH 输入框里
|
|
297
|
+
敲一条斜杠命令就能完成的用户面动作;持久目标还会把"一次请求"变成 harness 的轮次驱动会跨轮持续推进的
|
|
298
|
+
目标。这两条路本身都不消耗模型轮次。预期流程:
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
session_command_list { sessionId } # 该 agent 接受什么;permitted 标出放行集
|
|
302
|
+
-> session_command { sessionId, command: "/compact" } # 运行一条;不花模型轮次
|
|
303
|
+
-> session_goal { sessionId, action: "create", objective: "完成迁移", maxGoalRounds: 20 }
|
|
304
|
+
-> session_goal { sessionId, action: "get" } # 轮询 phase/revision……
|
|
305
|
+
或订阅 dsh://session/<id>/projections # ……或等它在变化时通知你
|
|
306
|
+
-> session_goal { sessionId, action: "pause", expectedRevision: 3 } # 之后 resume / complete / clear
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
`session_command_list` 报出每条命令的 `permitted` 标记而不是把被拒命令藏起来,调用方因此能看到该 profile
|
|
310
|
+
上确实有 `/permission` 与 `/feedback`、而本端点故意不运行它们:`/permission` 会改变会话的权限 preset,
|
|
311
|
+
`/feedback` 会写入用户自己的评审记录;两者都到不了 handler,也不会向会话日志写入任何东西。
|
|
312
|
+
`session_status` 报的是会话的轮次与日志状态,不含目标本身;目标的 phase 与 revision 来自
|
|
313
|
+
`session_goal { action: "get" }`,而 `dsh://session/<id>/projections` feed(其中含 `goal`)会在目标变化时
|
|
314
|
+
发布,因此跟进一个目标是轮询或订阅,而不是每轮都发 prompt。不带 `expectedRevision` 的修改作用于当时存在
|
|
315
|
+
的目标;若可能有两个监督者竞争,请传回读取时拿到的 revision。
|
|
316
|
+
|
|
291
317
|
## 工作树
|
|
292
318
|
|
|
293
319
|
一个会话可以在一份自己的、来自同一仓库的 Git 检出里工作。这正是 DSH 界面「New worktree」选项做的事:
|
|
@@ -328,7 +354,7 @@ profile 能读 session projection 时,`session_status` 会报 `workingDirector
|
|
|
328
354
|
|
|
329
355
|
MCP 服务无法主动调用客户端,所以「有什么变了」只能以资源通知的形式送出去。本端点把同一套变更 feed 发布到
|
|
330
356
|
两种标准的推送通道上,而且每条通知都**不携带负载**:它只点名一个合成资源,客户端再去读那个资源拿 JSON。
|
|
331
|
-
这件事没有新增任何工具——工具面仍是上面表格里的
|
|
357
|
+
这件事没有新增任何工具——工具面仍是上面表格里的 38 个——推送也没有配置键:它始终开启,且在无人订阅时
|
|
332
358
|
零成本。
|
|
333
359
|
|
|
334
360
|
| 客户端时代 | 握手 | 服务端→客户端通道 | 如何请求推送 |
|
|
@@ -880,6 +906,7 @@ profile 推导同一份映射;不过配置 profile 的人应当能在一处读
|
|
|
880
906
|
| `worktree_create` / `worktree_restore` / `session_create { worktree }` | `worktrees` **且** `sessionController.resolveAgent` | 服务由 DSH Desktop/Web 的 forge bundle(`@deepseek-ai/dsh-experimental-forge-bundle`)提供;裸 profile 没有它,`dsh_info.toolAvailability` 会把这些工具报成不可用,`session_create { worktree }` 在创建会话之前就被拒绝。会话的执行环境里还需要 Git 与 Node |
|
|
881
907
|
| `session_status.workingDirectory` / `session_status.worktree` / `session_list { includeWorktree }` | 可读的 session projection(`sessionController.projections` 或 `sessionQuery.observeSession`) | 可选:没有它时两个回复都省略这两个字段,其余内容不变 |
|
|
882
908
|
| `session_archive` / `session_unarchive` / `session_pin` / `session_unpin` | `workspaceRegistry.archiveSession` / `pinSession` | 与 DSH 侧边栏动作写入同一组 registry 集合;方法缺失时这些工具被报成不可用 |
|
|
909
|
+
| `session_command_list` / `session_command` / `session_goal` | `sessionController.resolveAgent` **且** `commands` / `goals` | 两个服务都是 agent 作用域的,会话的实时 agent 就是桥;profile 没有它们时 `dsh_info.toolAvailability` 会把这三个工具报成不可用,每次调用也会点名缺少的服务与提供它的 bundle |
|
|
883
910
|
| `workspace_*` | `workspaceRegistry` | 常规 profile 由 base bundle 提供 |
|
|
884
911
|
| `file_*` | `fs` | 部署的沙箱策略照样生效;`file_read_image` 还需要受支持的图片格式 |
|
|
885
912
|
| `shell_*` | `shell` | `shell_run` 有 `run()` 用 `run()`、否则 `execute()`;后台任务需要 `start()` 或 `execute()` |
|
|
@@ -1011,13 +1038,13 @@ node scripts/exercise.mjs # 约 60 项断言:建工作区、开会话、让
|
|
|
1011
1038
|
|
|
1012
1039
|
| 启用的工具组 | 公布工具数 | 最近结果 |
|
|
1013
1040
|
| --- | --- | --- |
|
|
1014
|
-
| 全部六组 |
|
|
1015
|
-
| 仅 `session`、`usage` |
|
|
1016
|
-
| `workspace`、`session`、`agentTools`、`usage` |
|
|
1041
|
+
| 全部六组 | 38 | `--with-model --with-subagent` 下 live-checks 195/195;验收 166/166,同一轮启动、搜索索引开启 |
|
|
1042
|
+
| 仅 `session`、`usage` | 23 | `--with-model` 下 live-checks 95/95(文件/shell/工作区段落跳过) |
|
|
1043
|
+
| `workspace`、`session`、`agentTools`、`usage` | 28 | live-checks 64/64(被关闭的组会在原因里点名 `tools.<group>` 开关) |
|
|
1017
1044
|
| 仅 `files`、`shell` | 11 | live-checks 68/68(会话/用量/队列/搜索段落跳过) |
|
|
1018
1045
|
|
|
1019
|
-
"公布工具数"一列是当前工具面(0.
|
|
1020
|
-
针对 0.52.86
|
|
1046
|
+
"公布工具数"一列是当前工具面(0.55.0:session 组 21 个、总计 38 个);"最近结果"一列记录的是
|
|
1047
|
+
针对 0.52.86(尚无 0.53.0 新增工具)的实测,此后未重测。
|
|
1021
1048
|
|
|
1022
1049
|
第五个形态 `mountonly` 以 `http.enabled: false` + `mountOnWebServer: true` 启动,经 DSH 自己的
|
|
1023
1050
|
web 服务器验证端点:109/109,全部廉价,其中包含 `dsh_info` 必须报 `listening: false`、
|
package/cordis.patch.yml
CHANGED
|
@@ -50,13 +50,14 @@
|
|
|
50
50
|
# stream) and cost nothing while no client subscribes. The session-group
|
|
51
51
|
# feeds need `session`; the job feeds need `shell`.
|
|
52
52
|
#
|
|
53
|
-
# The session group:
|
|
53
|
+
# The session group: 21 tools — create/list/search/status/prompt/
|
|
54
54
|
# transcript/tool-result/queue/cancel/fork/rename/set_model, the
|
|
55
|
-
# worktree pair,
|
|
55
|
+
# worktree pair, archive/unarchive + pin/unpin, and the orchestration
|
|
56
|
+
# trio (slash commands + durable goal). The worktree tools
|
|
56
57
|
# also need the worktrees service (the forge bundle in Desktop/Web,
|
|
57
58
|
# probed as `dsh_info.harnessServices.worktrees`); archive/pin write the
|
|
58
59
|
# same registry sets the DSH sidebar's actions write. Disabling the
|
|
59
|
-
# group removes all
|
|
60
|
+
# group removes all 21 at once; `dsh_info.toolAvailability` names the
|
|
60
61
|
# ones a profile cannot serve.
|
|
61
62
|
session: true
|
|
62
63
|
files: true
|
package/lib/index.d.ts
CHANGED
|
@@ -1482,6 +1482,19 @@ interface DshSessionEvent {
|
|
|
1482
1482
|
readonly time: number;
|
|
1483
1483
|
readonly data: unknown;
|
|
1484
1484
|
}
|
|
1485
|
+
/** One slash command a session's agent can run — `CommandDescriptor` in the 0.2.2 catalog. */
|
|
1486
|
+
interface DshCommandDescriptor {
|
|
1487
|
+
readonly name: string;
|
|
1488
|
+
readonly description: string;
|
|
1489
|
+
readonly input?: {
|
|
1490
|
+
readonly hint: string;
|
|
1491
|
+
readonly acceptsAttachments?: boolean;
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
/** Durable goal phase — `GoalPhase` in the 0.2.2 catalog. */
|
|
1495
|
+
type DshGoalPhase = 'active' | 'paused' | 'blocked' | 'complete';
|
|
1496
|
+
/** Whether the round driver is currently advancing the goal (`GoalActivation`). */
|
|
1497
|
+
type DshGoalActivation = 'armed' | 'disarmed';
|
|
1485
1498
|
/**
|
|
1486
1499
|
* One created worktree — `CreatedWorktree` in
|
|
1487
1500
|
* `packages/tool/worktree/src/index.ts` (harness 0.2.2-rc.1).
|
|
@@ -2063,6 +2076,35 @@ interface SessionEventFeed {
|
|
|
2063
2076
|
readonly asOfSeq: number;
|
|
2064
2077
|
readonly events: readonly SessionEventFeedEvent[];
|
|
2065
2078
|
}
|
|
2079
|
+
/** The reply of one slash-command execution. */
|
|
2080
|
+
interface SessionCommandResult {
|
|
2081
|
+
readonly sessionId: string;
|
|
2082
|
+
/** The harness's own command id for this execution; usable in the log/transcript. */
|
|
2083
|
+
readonly commandId: string;
|
|
2084
|
+
/** False when the handler itself reported an error result (`result.kind: 'error'`). */
|
|
2085
|
+
readonly ok: boolean;
|
|
2086
|
+
/** Text the handler returned, when it returned any. */
|
|
2087
|
+
readonly text?: string;
|
|
2088
|
+
/** The durable event the handler wrote, when it reported one. */
|
|
2089
|
+
readonly sourceEventSeq?: number;
|
|
2090
|
+
}
|
|
2091
|
+
/** One durable goal, as this endpoint projects it from the harness's `GoalView`. */
|
|
2092
|
+
interface SessionGoalView {
|
|
2093
|
+
readonly id: string;
|
|
2094
|
+
readonly revision: number;
|
|
2095
|
+
readonly objective: string;
|
|
2096
|
+
readonly phase: DshGoalPhase;
|
|
2097
|
+
/** Whether the round driver is currently advancing it. */
|
|
2098
|
+
readonly activation: DshGoalActivation;
|
|
2099
|
+
readonly maxGoalRounds: number;
|
|
2100
|
+
readonly roundsStarted: number;
|
|
2101
|
+
readonly blockedReason?: {
|
|
2102
|
+
readonly code: string;
|
|
2103
|
+
readonly message: string;
|
|
2104
|
+
};
|
|
2105
|
+
readonly createdAt: number;
|
|
2106
|
+
readonly updatedAt: number;
|
|
2107
|
+
}
|
|
2066
2108
|
/**
|
|
2067
2109
|
* One background shell job as a non-consuming feed snapshot.
|
|
2068
2110
|
*
|
|
@@ -2388,6 +2430,24 @@ export declare class DshDriver {
|
|
|
2388
2430
|
private workspaceRegistry;
|
|
2389
2431
|
private sessionController;
|
|
2390
2432
|
private sessionQuery;
|
|
2433
|
+
/**
|
|
2434
|
+
* `ctx.commands`, when the profile mounts the command registry.
|
|
2435
|
+
*
|
|
2436
|
+
* Every shipped DSH composition does (`dsh-base` mounts `@deepseek-ai/dsh-commands`
|
|
2437
|
+
* together with the built-in command packages), so a missing service means a
|
|
2438
|
+
* stripped-down profile rather than a version difference.
|
|
2439
|
+
*/
|
|
2440
|
+
private commandService;
|
|
2441
|
+
/**
|
|
2442
|
+
* `ctx.goals`, when the profile mounts the goal store.
|
|
2443
|
+
*
|
|
2444
|
+
* Shipped compositions mount it with `@deepseek-ai/dsh-tool-goal`; a profile that
|
|
2445
|
+
* drops the goal bundle has no goal surface at all, which is why the probe is
|
|
2446
|
+
* separate from `commands`.
|
|
2447
|
+
*/
|
|
2448
|
+
private goalService;
|
|
2449
|
+
private requireCommandService;
|
|
2450
|
+
private requireGoalService;
|
|
2391
2451
|
/**
|
|
2392
2452
|
* `ctx.worktrees`, when the profile mounts one.
|
|
2393
2453
|
*
|
|
@@ -2493,6 +2553,71 @@ export declare class DshDriver {
|
|
|
2493
2553
|
sessionId: string;
|
|
2494
2554
|
pinned: false;
|
|
2495
2555
|
}>;
|
|
2556
|
+
/**
|
|
2557
|
+
* The slash commands this session's agent can run, with the ones this endpoint
|
|
2558
|
+
* will actually execute marked.
|
|
2559
|
+
*
|
|
2560
|
+
* A command runs **without a model turn** (the handler is invoked directly), so
|
|
2561
|
+
* this is the cheap half of "do the thing a human would type into the composer".
|
|
2562
|
+
* `permitted` is reported rather than filtered out: a caller should be able to
|
|
2563
|
+
* see that `/permission` exists and that this endpoint deliberately will not run
|
|
2564
|
+
* it, instead of guessing why a name is missing.
|
|
2565
|
+
*/
|
|
2566
|
+
listSessionCommands(request: {
|
|
2567
|
+
sessionId: string;
|
|
2568
|
+
}): Promise<{
|
|
2569
|
+
sessionId: string;
|
|
2570
|
+
commands: readonly (DshCommandDescriptor & {
|
|
2571
|
+
readonly permitted: boolean;
|
|
2572
|
+
})[];
|
|
2573
|
+
}>;
|
|
2574
|
+
/**
|
|
2575
|
+
* Run one slash command against a session's agent.
|
|
2576
|
+
*
|
|
2577
|
+
* The allow-list is explicit and lives in {@link PERMITTED_COMMANDS}: the set of
|
|
2578
|
+
* commands a profile registers is not this endpoint's decision, and two of the
|
|
2579
|
+
* shipped ones are deliberately excluded (`/permission` escalates the session's
|
|
2580
|
+
* permission preset; `/feedback` writes the user's own review records). A
|
|
2581
|
+
* command not on the list is refused **before** the handler runs, so nothing is
|
|
2582
|
+
* logged into the session’s history for a refused call.
|
|
2583
|
+
*/
|
|
2584
|
+
runSessionCommand(request: {
|
|
2585
|
+
sessionId: string;
|
|
2586
|
+
command: string;
|
|
2587
|
+
signal?: AbortSignal;
|
|
2588
|
+
}): Promise<SessionCommandResult>;
|
|
2589
|
+
/**
|
|
2590
|
+
* The durable goal this session carries, if any.
|
|
2591
|
+
*
|
|
2592
|
+
* Goal state lives in the harness's own store and the round driver advances it
|
|
2593
|
+
* across turns, so reading it is how a supervising caller answers "what is this
|
|
2594
|
+
* session supposed to be doing, and is it paused".
|
|
2595
|
+
*/
|
|
2596
|
+
sessionGoal(request: {
|
|
2597
|
+
sessionId: string;
|
|
2598
|
+
}): Promise<{
|
|
2599
|
+
sessionId: string;
|
|
2600
|
+
goal: SessionGoalView | null;
|
|
2601
|
+
}>;
|
|
2602
|
+
/**
|
|
2603
|
+
* Create, edit, pause, resume, complete or clear a session's durable goal.
|
|
2604
|
+
*
|
|
2605
|
+
* `expectedRevision` is the lost-update guard: the harness's own `GoalRef`
|
|
2606
|
+
* carries a revision, and a mutation whose revision no longer matches is refused
|
|
2607
|
+
* rather than silently rewriting a goal the user edited in the UI a moment ago.
|
|
2608
|
+
*/
|
|
2609
|
+
mutateSessionGoal(request: {
|
|
2610
|
+
sessionId: string;
|
|
2611
|
+
action: 'create' | 'edit' | 'pause' | 'resume' | 'complete' | 'clear';
|
|
2612
|
+
objective?: string;
|
|
2613
|
+
maxGoalRounds?: number;
|
|
2614
|
+
expectedRevision?: number;
|
|
2615
|
+
}): Promise<{
|
|
2616
|
+
sessionId: string;
|
|
2617
|
+
action: string;
|
|
2618
|
+
goal: SessionGoalView | null;
|
|
2619
|
+
cleared?: true;
|
|
2620
|
+
}>;
|
|
2496
2621
|
private toWorkspaceInfo;
|
|
2497
2622
|
/**
|
|
2498
2623
|
* Explain a model-selection failure in terms the caller can act on.
|