dsh-as-mcp 0.55.0 → 0.56.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 +35 -0
- package/CHANGELOG.zh.md +7 -0
- package/README.md +30 -0
- package/README.zh.md +28 -1
- package/cordis.patch.yml +3 -3
- package/lib/index.d.ts +150 -0
- package/lib/index.js +344 -1
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,41 @@ 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.56.0
|
|
10
|
+
|
|
11
|
+
- Four new tools at the end of the `session` group — `schedule_create`, `schedule_list`,
|
|
12
|
+
`schedule_view` and `schedule_edit` — bring the surface from 38 to 42 (the group from 21 to 25).
|
|
13
|
+
They wrap the Host `ctx.schedule` service, so a task is stored and delivered by the harness itself:
|
|
14
|
+
its prompt fires at each occurrence whether or not this endpoint, or any client, is connected.
|
|
15
|
+
`schedule_create` takes exactly one timing form — `rrule` (one `DTSTART` line plus one `RRULE` line,
|
|
16
|
+
the same RFC 5545 subset the DSH Automation page writes), `afterSeconds` for a one-shot N seconds
|
|
17
|
+
from now, or `everySeconds` for a fixed interval — and a `target` of `"session"` (update an existing
|
|
18
|
+
conversation, needs `sessionId`), `"new_once"` (create one conversation on the first delivery and
|
|
19
|
+
reuse it) or `"new"` (a fresh conversation per delivery), with `cwd`/`agentPreset` on the two
|
|
20
|
+
new-conversation targets. A request that names two timing forms is refused rather than silently
|
|
21
|
+
preferring one, and a new-conversation target that also names a `sessionId` is refused as
|
|
22
|
+
self-contradicting. `schedule_list` pages tasks (active first by next occurrence, `limit` 1–200);
|
|
23
|
+
`schedule_view` answers `found: false` for a missing id instead of an error; `schedule_edit`
|
|
24
|
+
updates or deletes, pauses with `active: false` and resumes with `active: true`, and refuses change
|
|
25
|
+
fields on a delete.
|
|
26
|
+
- **The whole schedule surface is refused while `session.scope` is `"owned"`.** A scheduled task is a
|
|
27
|
+
Host record with no owning session, so an ownership filter cannot be expressed on it, and its
|
|
28
|
+
delivery target may name any conversation on the instance; the error says exactly that and points at
|
|
29
|
+
`scope: "all"`. Mutation outcomes are data rather than errors — `{ updated: true, task }`,
|
|
30
|
+
`{ deleted: true }`, `{ found: false }`, `{ conflict: true }` — so a polling caller can act without
|
|
31
|
+
parsing text, and `expectedUpdatedAt` carries the harness's own revision: a stale edit answers
|
|
32
|
+
`conflict: true` and changes nothing instead of overwriting what the user just changed in the
|
|
33
|
+
Automation page.
|
|
34
|
+
- `dsh_info.harnessServices` gained a `schedule` probe (30 probes, was 29) and `dsh_info.notes` a
|
|
35
|
+
paragraph on the Host-global nature and the owned-scope refusal. No new configuration keys: the
|
|
36
|
+
push surface, both transports, the tool groups and the settings panel are unchanged.
|
|
37
|
+
- Docs and tests: both READMEs gain four tool rows and a "Scheduled automation" section (with the
|
|
38
|
+
create → list → pause/resume → delete flow and the owned-scope warning), `docs/CAPABILITY-GAPS.md`
|
|
39
|
+
moves to 42 tools / 30 probes, and `cordis.patch.yml` restates the session group as 25 tools.
|
|
40
|
+
`tests/schedule.test.ts` pins the driver contract (one timing form, target rules, page bounds, the
|
|
41
|
+
owned-scope refusal, conflict/miss as data); `tests/mcp-endpoint.test.ts` pins argument routing;
|
|
42
|
+
`tests/availability.test.ts` covers the new required capability.
|
|
43
|
+
|
|
9
44
|
## 0.55.0
|
|
10
45
|
|
|
11
46
|
- Three new tools in the `session` group — `session_command_list`, `session_command` and
|
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.56.0
|
|
10
|
+
|
|
11
|
+
- 在 session 组末尾新增 4 个工具(`schedule_create`、`schedule_list`、`schedule_view`、`schedule_edit`),工具面从 38 增至 42(该组从 21 增至 25)。它们包装 Host 的 `ctx.schedule` 服务,任务由 harness 自己存储与投递:每次触发都把 prompt 送到目标会话,无论本端点或任何客户端是否在线。`schedule_create` 必须且只能给一种时间形态——`rrule`(一行 `DTSTART` 加一行 `RRULE`,与 DSH「自动化」页面写出的 RFC 5545 子集相同)、`afterSeconds`(从现在起 N 秒后一次)、或 `everySeconds`(固定间隔)——`target` 取 `"session"`(更新已有会话,需要 `sessionId`)、`"new_once"`(首次投递新建会话并复用)、`"new"`(每次投递新建会话),后两者可带 `cwd`/`agentPreset`。同时给两种时间形态会被拒绝而不是悄悄取其一;`target` 为新建会话却又给了 `sessionId` 也会因自相矛盾被拒绝。`schedule_list` 分页列出任务(活动任务按下次触发在前,`limit` 1–200);`schedule_view` 对不存在的 id 返回 `found: false` 而不是报错;`schedule_edit` 负责更新/删除,`active: false` 暂停、`true` 恢复,删除时拒绝任何改动字段。
|
|
12
|
+
- **`session.scope` 为 `"owned"` 时整个 schedule 面被拒绝。** 定时任务是 Host 记录、没有所属会话,无法在其上表达归属过滤,而它的投递目标可以指向实例上的任意会话;错误信息会说明原因并指向 `scope: "all"`。修改结果用数据表达而不是抛错——`{ updated: true, task }`、`{ deleted: true }`、`{ found: false }`、`{ conflict: true }`——轮询调用方不必解析文本;`expectedUpdatedAt` 携带 harness 自己的 revision:过期编辑返回 `conflict: true` 且不做修改,不会覆盖用户刚在「自动化」页面改过的东西。
|
|
13
|
+
- `dsh_info.harnessServices` 新增 `schedule` 探针(共 30 个,原为 29 个),`dsh_info.notes` 增加一段说明 Host 级属性与 owned 拒绝。无新增配置键:推送、两种传输、工具组与设置面板均不变。
|
|
14
|
+
- 文档与测试:两份 README 增加 4 行工具表与「定时自动化 / Scheduled automation」小节(含 create → list → 暂停/恢复 → delete 流程与 owned 警告),`docs/CAPABILITY-GAPS.md` 更新为 42 工具 / 30 探针,`cordis.patch.yml` 把 session 组注释改为 25 个工具。`tests/schedule.test.ts` 钉住驱动契约(唯一时间形态、target 规则、分页上限、owned 拒绝、冲突/未找到以数据表达);`tests/mcp-endpoint.test.ts` 钉住参数透传;`tests/availability.test.ts` 覆盖新的依赖能力。
|
|
15
|
+
|
|
9
16
|
## 0.55.0
|
|
10
17
|
|
|
11
18
|
- 新增 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` 与文本,注册表未接收的名字/参数返回「什么都没写进日志」的错误。
|
package/README.md
CHANGED
|
@@ -255,6 +255,10 @@ schema-aware client can read the object directly instead of parsing JSON out of
|
|
|
255
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
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
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. |
|
|
258
|
+
| `schedule_create` | Create a **Host** scheduled task: an instruction the harness delivers to a conversation at each occurrence, whether or not this endpoint or any client is connected. Give exactly one timing form — `rrule` (one `DTSTART` line plus one `RRULE` line, the RFC 5545 subset the DSH Automation page writes, e.g. `"DTSTART;TZID=Asia/Singapore:20260901T080000\nRRULE:FREQ=DAILY;COUNT=3"`), `afterSeconds` for a one-shot N seconds from now, or `everySeconds` for a fixed interval. `target` decides where each delivery lands: `"session"` updates an existing conversation (needs `sessionId`), `"new_once"` creates one conversation on the first delivery and reuses it, `"new"` starts a fresh conversation every time; the two new-conversation targets accept `cwd` and `agentPreset`. The default target is `"session"` when `sessionId` is given and `"new"` otherwise. The reply is the stored task with its generated id, `scheduledAt`, `status` and timing variant fields. Tasks are Host records, not session records, so this surface is refused while `session.scope` is `"owned"`. |
|
|
259
|
+
| `schedule_list` | One page of Host scheduled tasks, active ones first by next occurrence and paused/ended ones after. `active: true` keeps only active tasks, `false` only paused and ended ones; absent returns every task. `limit` is 1–200 (default 20) and `offset` skips rows. Each row carries the task `id`, `title`, `prompt`, its timing fields, `scheduledAt`, `status`, `target` and the last delivery once it has fired. Lists tasks any session created; refused while `session.scope` is `"owned"`. |
|
|
260
|
+
| `schedule_view` | One Host scheduled task by `scheduleId`, with the same fields `schedule_list` reports plus its delivery target detail. An id that no longer exists answers `{ "found": false, "id": ... }` rather than an error, so a cleanup loop stays idempotent. Refused while `session.scope` is `"owned"`. |
|
|
261
|
+
| `schedule_edit` | Change or remove one Host scheduled task. `action: "update"` needs at least one of `title`, `prompt`, a timing form (`rrule`, `afterSeconds` or `everySeconds`), a `target` (with `sessionId` for `"session"`, or `cwd`/`agentPreset` for `"new"`/`"new_once"`), or `active` — `false` pauses the task, `true` resumes it, and a resume may need a new timing when the old rule is exhausted. `action: "delete"` removes the task and its saved delivery records and accepts no change fields. `expectedUpdatedAt` is the lost-update guard: if the task changed after that read the call answers `conflict: true` and changes nothing. Outcomes are data, not errors: `{ updated: true, task }`, `{ deleted: true }`, `{ found: false }` or `{ conflict: true }`. Refused while `session.scope` is `"owned"`. |
|
|
258
262
|
| `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. |
|
|
259
263
|
| `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. |
|
|
260
264
|
| `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. |
|
|
@@ -341,6 +345,32 @@ goal is a poll or a subscription rather than a prompt per turn. A mutation witho
|
|
|
341
345
|
acts on the goal that exists at that moment; pass the revision a read returned when two supervisors
|
|
342
346
|
could race.
|
|
343
347
|
|
|
348
|
+
### Scheduled automation
|
|
349
|
+
|
|
350
|
+
A scheduled task is the one surface here that outlives the process: the harness stores it and delivers
|
|
351
|
+
its prompt at each occurrence, so it keeps working when no MCP client is connected. Tasks are **Host**
|
|
352
|
+
records rather than session records — `schedule_list` shows tasks any session created, and `target`
|
|
353
|
+
decides whether a delivery updates one conversation, reuses a conversation created on first delivery, or
|
|
354
|
+
starts a fresh one each time:
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
schedule_create { title: "Daily dependency check", prompt: "Run the dependency audit and report",
|
|
358
|
+
rrule: "DTSTART;TZID=Asia/Singapore:20260901T080000\nRRULE:FREQ=DAILY",
|
|
359
|
+
target: "new_once", cwd: "/repo" }
|
|
360
|
+
schedule_create { title: "Check the build in ten minutes", prompt: "Check whether the build is green",
|
|
361
|
+
afterSeconds: 600, target: "session", sessionId }
|
|
362
|
+
-> schedule_list { active: true } # id, next occurrence, status
|
|
363
|
+
-> schedule_edit { scheduleId, action: "update", active: false } # pause
|
|
364
|
+
-> schedule_edit { scheduleId, action: "update", title: "..." , everySeconds: 3600 }
|
|
365
|
+
-> schedule_edit { scheduleId, action: "delete" } # removes the task and its deliveries
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Because a Host record has no owning session, this whole surface is **refused while `session.scope` is
|
|
369
|
+
`"owned"`**: an ownership filter cannot be expressed on it, and a task's delivery target may name any
|
|
370
|
+
conversation on the instance. The refusal says so and points at `scope: "all"`. `expectedUpdatedAt`
|
|
371
|
+
carries the harness's own revision, so a stale edit answers `conflict: true` and changes nothing instead
|
|
372
|
+
of overwriting what the user just changed in the Automation page.
|
|
373
|
+
|
|
344
374
|
## Worktrees
|
|
345
375
|
|
|
346
376
|
A session can work in its own Git checkout of the same repository. The operation is the one the
|
package/README.zh.md
CHANGED
|
@@ -240,6 +240,10 @@ scheme 可以省略:`127.0.0.1:8790/mcp` 也能识别。
|
|
|
240
240
|
| `session_command_list` | 列出该会话 agent 能运行的斜杠命令——每条带一句描述、可选的 `input` 提示与 `permitted` 标记——因此调用方能看到某个命令存在、而本端点故意不运行它(放行集是 `/compact`、`/plan`、`/goal`、`/export`;`/permission` 与 `/feedback` 刻意拒绝)。命令**不消耗模型轮次**(直接调用 handler),所以这是压缩长会话、导出日志、进入 plan 模式或设置目标最便宜的办法——与人在 DSH 输入框里敲命令完全一致。只读;用 `session_command` 运行其中一条。 |
|
|
241
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
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` 返回已完成的目标。 |
|
|
243
|
+
| `schedule_create` | 创建一个 **Host 级** 定时任务:harness 会在每次触发时把指令投递到某个会话,无论本端点或任何客户端是否在线。时必须且只能给出一种形式——`rrule`(一行 `DTSTART` 加一行 `RRULE`,即 DSH「自动化」页面写出的 RFC 5545 子集,例如 `"DTSTART;TZID=Asia/Singapore:20260901T080000\nRRULE:FREQ=DAILY;COUNT=3"`)、`afterSeconds`(从现在起 N 秒后执行一次)、或 `everySeconds`(固定间隔)。`target` 决定投递落点:`"session"` 更新一个已有会话(需要 `sessionId`)、`"new_once"` 首次投递时新建一个会话并复用、`"new"` 每次投递都新建会话;后两种可带 `cwd` 与 `agentPreset`。给了 `sessionId` 时默认 `"session"`,否则默认 `"new"`。返回存下的任务,含生成的 id、`scheduledAt`、`status` 与具体时间形态字段。任务属于 Host 记录而不是会话记录,因此当 `session.scope` 为 `"owned"` 时整个 schedule 面会被拒绝。 |
|
|
244
|
+
| `schedule_list` | Host 定时任务的一页,活动任务按下次触发时间在前,暂停/已结束的在后。`active: true` 只留活动任务,`false` 只留暂停与已结束的;不传则返回全部。`limit` 为 1–200(默认 20),`offset` 用于跳过。每行含任务 `id`、`title`、`prompt`、时间形态字段、`scheduledAt`、`status`、`target`,以及触发过之后的最近一次投递。列出的是任何会话创建的任务;`session.scope` 为 `"owned"` 时拒绝。 |
|
|
245
|
+
| `schedule_view` | 按 `scheduleId` 读一个 Host 定时任务,字段与 `schedule_list` 相同并附投递目标细节。已不存在的 id 返回 `{ "found": false, "id": ... }` 而不是报错,清理循环因此保持幂等。`session.scope` 为 `"owned"` 时拒绝。 |
|
|
246
|
+
| `schedule_edit` | 修改或删除一个 Host 定时任务。`action: "update"` 至少需要一项改动:`title`、`prompt`、某种时间形态(`rrule`、`afterSeconds` 或 `everySeconds`)、`target`(`"session"` 需 `sessionId`,`"new"`/`"new_once"` 可带 `cwd`/`agentPreset`),或 `active`——`false` 暂停,`true` 恢复,旧规则已耗尽时恢复可能需要新的时间形态。`action: "delete"` 删除任务及其保存的投递记录,且不接受任何改动字段。`expectedUpdatedAt` 是丢失更新的守卫:若任务在该次读取后变过,调用返回 `conflict: true` 且不做任何修改。结果是数据而不是错误:`{ updated: true, task }`、`{ deleted: true }`、`{ found: false }` 或 `{ conflict: true }`。`session.scope` 为 `"owned"` 时拒绝。 |
|
|
243
247
|
| `session_usage` | 按会话统计 token 用量(默认含其 subagent):精确的四桶计数、按模型拆分、可选的按轮拆分,装了价格插件时还有钱。传 `sessionIds` 可一次结算多个已知会话,传 `cwd` 可汇总某个工作目录里跑过的所有会话。 |
|
|
244
248
|
| `file_read` | 经 DSH 文件系统服务读 UTF-8 文件。受字节上限约束;`offset`/`nextOffset` 可翻页读取更大的文件(两者都是**字节**偏移,因此 EOF 处或之后的偏移是空的完整读取),`maxBytes` 用更小的窗口降低单次开销;`sha256: true` 返回整个文件的哈希,作为后续写入/编辑的比较并交换值。 |
|
|
245
249
|
| `file_read_image` | 读取图片(png/jpeg/gif/webp/bmp,按字节嗅探)并以 MCP image block 返回,让你直接看截图或图表而不是靠猜。上限 8 MiB。 |
|
|
@@ -314,6 +318,29 @@ session_command_list { sessionId } # 该 agen
|
|
|
314
318
|
发布,因此跟进一个目标是轮询或订阅,而不是每轮都发 prompt。不带 `expectedRevision` 的修改作用于当时存在
|
|
315
319
|
的目标;若可能有两个监督者竞争,请传回读取时拿到的 revision。
|
|
316
320
|
|
|
321
|
+
### 定时自动化
|
|
322
|
+
|
|
323
|
+
定时任务是本插件里唯一比进程活得更久的面:harness 负责存储并在每次触发时投递 prompt,因此即使没有
|
|
324
|
+
任何 MCP 客户端在线也照常工作。任务是 **Host** 记录而不是会话记录——`schedule_list` 列出任何会话创建的
|
|
325
|
+
任务,`target` 决定投递是更新同一个会话、首次投递时新建一个会话再复用、还是每次新建一个会话:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
schedule_create { title: "每日依赖检查", prompt: "跑一遍依赖审计并汇报",
|
|
329
|
+
rrule: "DTSTART;TZID=Asia/Singapore:20260901T080000\nRRULE:FREQ=DAILY",
|
|
330
|
+
target: "new_once", cwd: "/repo" }
|
|
331
|
+
schedule_create { title: "十分钟后看构建", prompt: "看看构建是否变绿",
|
|
332
|
+
afterSeconds: 600, target: "session", sessionId }
|
|
333
|
+
-> schedule_list { active: true } # id、下次触发、状态
|
|
334
|
+
-> schedule_edit { scheduleId, action: "update", active: false } # 暂停
|
|
335
|
+
-> schedule_edit { scheduleId, action: "update", title: "..." , everySeconds: 3600 }
|
|
336
|
+
-> schedule_edit { scheduleId, action: "delete" } # 连任务与投递记录一起删除
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
因为 Host 记录没有所属会话,`session.scope` 为 `"owned"` 时整个面色会被**拒绝**:这种记录上无法表达
|
|
340
|
+
归属过滤,而任务的投递目标可以指向实例上的任意会话。拒绝信息会说明原因并指向 `scope: "all"`。
|
|
341
|
+
`expectedUpdatedAt` 携带 harness 自己的 revision,因此过期编辑返回 `conflict: true` 且不做修改,而不是
|
|
342
|
+
覆盖用户刚在「自动化」页面里改过的东西。
|
|
343
|
+
|
|
317
344
|
## 工作树
|
|
318
345
|
|
|
319
346
|
一个会话可以在一份自己的、来自同一仓库的 Git 检出里工作。这正是 DSH 界面「New worktree」选项做的事:
|
|
@@ -354,7 +381,7 @@ profile 能读 session projection 时,`session_status` 会报 `workingDirector
|
|
|
354
381
|
|
|
355
382
|
MCP 服务无法主动调用客户端,所以「有什么变了」只能以资源通知的形式送出去。本端点把同一套变更 feed 发布到
|
|
356
383
|
两种标准的推送通道上,而且每条通知都**不携带负载**:它只点名一个合成资源,客户端再去读那个资源拿 JSON。
|
|
357
|
-
|
|
384
|
+
这件事没有新增任何工具——工具面是上面表格里的 42 个——推送也没有配置键:它始终开启,且在无人订阅时
|
|
358
385
|
零成本。
|
|
359
386
|
|
|
360
387
|
| 客户端时代 | 握手 | 服务端→客户端通道 | 如何请求推送 |
|
package/cordis.patch.yml
CHANGED
|
@@ -50,14 +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: 25 tools — create/list/search/status/prompt/
|
|
54
54
|
# transcript/tool-result/queue/cancel/fork/rename/set_model, the
|
|
55
55
|
# worktree pair, archive/unarchive + pin/unpin, and the orchestration
|
|
56
|
-
# trio (slash commands + durable goal). The worktree tools
|
|
56
|
+
# trio (slash commands + durable goal) and the schedule quartet. The worktree tools
|
|
57
57
|
# also need the worktrees service (the forge bundle in Desktop/Web,
|
|
58
58
|
# probed as `dsh_info.harnessServices.worktrees`); archive/pin write the
|
|
59
59
|
# same registry sets the DSH sidebar's actions write. Disabling the
|
|
60
|
-
# group removes all
|
|
60
|
+
# group removes all 25 at once; `dsh_info.toolAvailability` names the
|
|
61
61
|
# ones a profile cannot serve.
|
|
62
62
|
session: true
|
|
63
63
|
files: true
|
package/lib/index.d.ts
CHANGED
|
@@ -1482,6 +1482,63 @@ interface DshSessionEvent {
|
|
|
1482
1482
|
readonly time: number;
|
|
1483
1483
|
readonly data: unknown;
|
|
1484
1484
|
}
|
|
1485
|
+
/** The resolved target of a stored task — `ScheduleTarget`. */
|
|
1486
|
+
type DshScheduleTarget = {
|
|
1487
|
+
readonly kind: 'session';
|
|
1488
|
+
readonly sessionId: string;
|
|
1489
|
+
} | {
|
|
1490
|
+
readonly kind: 'new_once';
|
|
1491
|
+
readonly sessionId: string;
|
|
1492
|
+
readonly create: {
|
|
1493
|
+
readonly cwd?: string;
|
|
1494
|
+
readonly agentPreset?: string;
|
|
1495
|
+
};
|
|
1496
|
+
} | {
|
|
1497
|
+
readonly kind: 'new';
|
|
1498
|
+
readonly create: {
|
|
1499
|
+
readonly cwd?: string;
|
|
1500
|
+
readonly agentPreset?: string;
|
|
1501
|
+
};
|
|
1502
|
+
};
|
|
1503
|
+
/**
|
|
1504
|
+
* One stored scheduled task — `ScheduleCatalogEntry` in the 0.2.2 catalog.
|
|
1505
|
+
*
|
|
1506
|
+
* Only the fields this endpoint reports are declared; the timing variant fields
|
|
1507
|
+
* (`afterSeconds`, `everySeconds`, `rrule`, `expression`/`timeZone`) arrive on the
|
|
1508
|
+
* same object and are projected through {@link DshScheduleEntry}.
|
|
1509
|
+
*/
|
|
1510
|
+
interface DshScheduleEntry {
|
|
1511
|
+
readonly id: string;
|
|
1512
|
+
readonly kind: 'after' | 'at' | 'every' | 'daily' | 'weekly' | 'cron' | 'rrule';
|
|
1513
|
+
readonly title: string;
|
|
1514
|
+
readonly prompt: string;
|
|
1515
|
+
readonly scheduledAt: string;
|
|
1516
|
+
readonly scheduledAtLocal?: string;
|
|
1517
|
+
readonly status: 'active' | 'paused' | 'ended';
|
|
1518
|
+
readonly target: DshScheduleTarget;
|
|
1519
|
+
readonly involvedSessionIds?: readonly string[];
|
|
1520
|
+
readonly createdAt?: string;
|
|
1521
|
+
readonly updatedAt?: string;
|
|
1522
|
+
readonly executionActive?: true;
|
|
1523
|
+
readonly lastDelivery?: {
|
|
1524
|
+
readonly scheduledAt: string;
|
|
1525
|
+
readonly deliveredAt: string;
|
|
1526
|
+
readonly targetSessionId: string;
|
|
1527
|
+
};
|
|
1528
|
+
readonly afterSeconds?: number;
|
|
1529
|
+
readonly everySeconds?: number;
|
|
1530
|
+
readonly rrule?: string;
|
|
1531
|
+
readonly expression?: string;
|
|
1532
|
+
readonly timeZone?: string;
|
|
1533
|
+
}
|
|
1534
|
+
/** One page of tasks — `ScheduleListResult`. */
|
|
1535
|
+
interface DshScheduleListResult {
|
|
1536
|
+
readonly items: readonly DshScheduleEntry[];
|
|
1537
|
+
readonly shown: number;
|
|
1538
|
+
readonly total: number;
|
|
1539
|
+
readonly limit: number;
|
|
1540
|
+
readonly offset: number;
|
|
1541
|
+
}
|
|
1485
1542
|
/** One slash command a session's agent can run — `CommandDescriptor` in the 0.2.2 catalog. */
|
|
1486
1543
|
interface DshCommandDescriptor {
|
|
1487
1544
|
readonly name: string;
|
|
@@ -2438,6 +2495,25 @@ export declare class DshDriver {
|
|
|
2438
2495
|
* stripped-down profile rather than a version difference.
|
|
2439
2496
|
*/
|
|
2440
2497
|
private commandService;
|
|
2498
|
+
/**
|
|
2499
|
+
* `ctx.schedule`, when the profile mounts the Host task store.
|
|
2500
|
+
*
|
|
2501
|
+
* Shipped Web compositions mount it (`dsh-web-app` composes `@deepseek-ai/dsh-schedule`),
|
|
2502
|
+
* but a bare CLI profile may not, and every method here is used through this guard
|
|
2503
|
+
* so a missing service degrades to a capability error instead of a crash.
|
|
2504
|
+
*/
|
|
2505
|
+
private scheduleService;
|
|
2506
|
+
private requireScheduleService;
|
|
2507
|
+
/**
|
|
2508
|
+
* Refuse the host-global task surface under an owned scope.
|
|
2509
|
+
*
|
|
2510
|
+
* A scheduled task is a Host record: it has no owner session, `edit` reaches it by
|
|
2511
|
+
* bare id, and its target may be any conversation on this instance. `session.scope:
|
|
2512
|
+
* "owned"` exists to keep a handed-out token inside the sessions this endpoint
|
|
2513
|
+
* created, and that promise cannot be kept for a record with no owner field — so
|
|
2514
|
+
* the whole surface is refused rather than partially filtered.
|
|
2515
|
+
*/
|
|
2516
|
+
private assertScheduleSurfaceAllowed;
|
|
2441
2517
|
/**
|
|
2442
2518
|
* `ctx.goals`, when the profile mounts the goal store.
|
|
2443
2519
|
*
|
|
@@ -2618,6 +2694,80 @@ export declare class DshDriver {
|
|
|
2618
2694
|
goal: SessionGoalView | null;
|
|
2619
2695
|
cleared?: true;
|
|
2620
2696
|
}>;
|
|
2697
|
+
/**
|
|
2698
|
+
* Create one Host scheduled task.
|
|
2699
|
+
*
|
|
2700
|
+
* The timing is given as an RRULE string (the canonical form the DSH UI writes),
|
|
2701
|
+
* or as one of the two simple shorthands the service also accepts (`afterSeconds`
|
|
2702
|
+
* for a one-shot, `everySeconds` for a fixed interval). Exactly one form is used;
|
|
2703
|
+
* asking for two would leave which one wins to the implementation.
|
|
2704
|
+
*
|
|
2705
|
+
* The target decides where each delivery lands: an existing conversation
|
|
2706
|
+
* (`session`), a conversation created on the first delivery and reused
|
|
2707
|
+
* (`new_once`), or a fresh conversation per delivery (`new`).
|
|
2708
|
+
*/
|
|
2709
|
+
createScheduledTask(request: {
|
|
2710
|
+
title: string;
|
|
2711
|
+
prompt: string;
|
|
2712
|
+
rrule?: string;
|
|
2713
|
+
afterSeconds?: number;
|
|
2714
|
+
everySeconds?: number;
|
|
2715
|
+
target?: 'session' | 'new_once' | 'new';
|
|
2716
|
+
sessionId?: string;
|
|
2717
|
+
cwd?: string;
|
|
2718
|
+
agentPreset?: string;
|
|
2719
|
+
}): Promise<{
|
|
2720
|
+
task: DshScheduleEntry;
|
|
2721
|
+
}>;
|
|
2722
|
+
/**
|
|
2723
|
+
* One page of Host scheduled tasks, active first by next occurrence.
|
|
2724
|
+
*
|
|
2725
|
+
* `active` filters by state: true keeps only active tasks, false only paused and
|
|
2726
|
+
* ended ones. Absent returns everything.
|
|
2727
|
+
*/
|
|
2728
|
+
listScheduledTasks(request?: {
|
|
2729
|
+
active?: boolean;
|
|
2730
|
+
limit?: number;
|
|
2731
|
+
offset?: number;
|
|
2732
|
+
}): Promise<DshScheduleListResult>;
|
|
2733
|
+
/** One task by id, or a `found: false` answer for an id that no longer exists. */
|
|
2734
|
+
viewScheduledTask(request: {
|
|
2735
|
+
scheduleId: string;
|
|
2736
|
+
}): Promise<{
|
|
2737
|
+
found: boolean;
|
|
2738
|
+
id: string;
|
|
2739
|
+
task?: DshScheduleEntry;
|
|
2740
|
+
}>;
|
|
2741
|
+
/**
|
|
2742
|
+
* Update or delete one task.
|
|
2743
|
+
*
|
|
2744
|
+
* `expectedUpdatedAt` is the lost-update guard: an update or delete whose task
|
|
2745
|
+
* changed after that read reports a conflict and changes nothing. `active: false`
|
|
2746
|
+
* pauses and `active: true` resumes — a resume needs a future occurrence, which is
|
|
2747
|
+
* why the harness may refuse it for an exhausted rule.
|
|
2748
|
+
*/
|
|
2749
|
+
editScheduledTask(request: {
|
|
2750
|
+
scheduleId: string;
|
|
2751
|
+
action: 'update' | 'delete';
|
|
2752
|
+
title?: string;
|
|
2753
|
+
prompt?: string;
|
|
2754
|
+
rrule?: string;
|
|
2755
|
+
afterSeconds?: number;
|
|
2756
|
+
everySeconds?: number;
|
|
2757
|
+
target?: 'session' | 'new_once' | 'new';
|
|
2758
|
+
sessionId?: string;
|
|
2759
|
+
cwd?: string;
|
|
2760
|
+
agentPreset?: string;
|
|
2761
|
+
active?: boolean;
|
|
2762
|
+
expectedUpdatedAt?: string | null;
|
|
2763
|
+
}): Promise<{
|
|
2764
|
+
updated?: true;
|
|
2765
|
+
deleted?: true;
|
|
2766
|
+
found: boolean;
|
|
2767
|
+
conflict?: true;
|
|
2768
|
+
id: string;
|
|
2769
|
+
task?: DshScheduleEntry;
|
|
2770
|
+
}>;
|
|
2621
2771
|
private toWorkspaceInfo;
|
|
2622
2772
|
/**
|
|
2623
2773
|
* Explain a model-selection failure in terms the caller can act on.
|