dsh-autorestart 0.6.0 → 0.6.2
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/README.md +150 -34
- package/client.js +805 -133
- package/cordis.patch.yml +18 -8
- package/docs/README.zh.md +93 -21
- package/index.js +456 -74
- package/lib/activity.js +124 -0
- package/lib/lifecycle.js +17 -4
- package/lib/queued.js +146 -0
- package/lib/semver.js +49 -0
- package/locale/en.json +10 -6
- package/locale/zh.json +10 -6
- package/package.json +1 -1
package/cordis.patch.yml
CHANGED
|
@@ -25,14 +25,24 @@
|
|
|
25
25
|
relaunchDelaySeconds: 2
|
|
26
26
|
# quiet window (ms) after the last conversation activity before a restart may fire
|
|
27
27
|
settleMs: 2000
|
|
28
|
-
#
|
|
29
|
-
# before the host is really restarted
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
|
|
28
|
+
# RESTART reserve (ms): the extra wait after the LAST conversation
|
|
29
|
+
# finished, before the host is really restarted. A restart is a daily
|
|
30
|
+
# operation, so the default is 0 (no extra wait). A DROPDOWN in the
|
|
31
|
+
# settings page: 0 / 5 / 10 / 20 / 30 / 60 seconds.
|
|
32
|
+
# This plugin can only see the DSH agents — other plugins (a memory
|
|
33
|
+
# plugin that writes its notes a few seconds later, a summary, an MCP
|
|
34
|
+
# callback) queue their work with a timer of their own, and that work
|
|
35
|
+
# is invisible here.
|
|
36
|
+
restartGraceMs: 0
|
|
37
|
+
# The graceful shutdown has NO reserve of its own: the visible
|
|
38
|
+
# countdown (sleepCountdownSeconds) IS the wait before the machine
|
|
39
|
+
# goes off.
|
|
40
|
+
# Upper bound (ms) of a QUEUED inbox message: a message that nobody
|
|
41
|
+
# claims within this window is stuck, so it stops blocking the
|
|
42
|
+
# restart (and the session that holds it is named in the log).
|
|
43
|
+
# Without it a single unclaimed message kept the restart pending for
|
|
44
|
+
# ever. `0` switches the protection off (the old behaviour).
|
|
45
|
+
queuedGraceMs: 60000
|
|
36
46
|
# dsh web: reload the page when the host boot token changes
|
|
37
47
|
autoReloadPage: true
|
|
38
48
|
# hard-block shell commands that would kill/relaunch DSH by hand
|
package/docs/README.zh.md
CHANGED
|
@@ -98,10 +98,13 @@ session resume on boot, durable-goal re-arm, and an agent-callable idle-aware gr
|
|
|
98
98
|
> **知道了**
|
|
99
99
|
|
|
100
100
|
- **只有升级才会看到**:宿主把「这个功能的版本」和「已经展示过的版本」比一比(记在检查点文件的 `seenFeatureVersions` 里)。**新装**的用户不会看到这张卡 —— 安装提问已经把功能讲清楚了,答完就等于"已看过"。
|
|
101
|
-
-
|
|
101
|
+
- **「看过」= 记录版本不低于当前功能版本,不是「等于」**:记录的版本只要**不低于**当前的功能版本就算看过 —— 功能停在 `0.5.0` 时,记录是 `0.6.0` 也算看过。**为什么不按等值判**:记录里写的是「当时公告带的版本」,而 0.6.0 之前公告带的是**包版本**,所以按等值判就会告诉一个早就点过「知道了」的用户「你还没见过」;那条记录只说明他在哪个版本上点过,不说明他没看过这个功能。记录根本不是版本号(比如坏记录)时也算看过 —— 宁可少打扰,也不要因为一条坏记录天天弹卡片。
|
|
102
|
+
- **每个功能版本只弹一次**:点「知道了」→ 宿主记下版本 → 卡片立刻消失 → **以后再也不出现**(重启宿主不会,升级发下一个版本也不会 —— 除非功能的版本比记录的更新,那时才再公告一次)。
|
|
103
|
+
- **公告跟的是「功能」,不是「发版」**:公告带的版本号是源码里的常量 `SLEEP_FEATURE_VERSION`(`index.js`),**不是** `package.json` 里的包版本 —— 所以发版不会把老用户已经点过「知道了」的卡片再弹回来。(0.6.0 之前公告带的是包版本,于是 0.5.0 → 0.6.0 每发一次版,「收工关机」就重弹一次。)
|
|
104
|
+
- **想主动再公告一次**:把 `SLEEP_FEATURE_VERSION` 调到**比最后记录的版本更新**的数即可 —— 卡片就再出现一次。但**只在功能本身变化大到必须再讲一遍时**才调(比如关机改成了完全不同的用法、按钮换了位置);**发版、修 bug、加小功能都不要动它** —— 那正是这个常量要防的坑。
|
|
102
105
|
- **没点不会静默吞掉**:卡片一直挂在那里等你回答;不回答,下次启动还会再出现一次。
|
|
103
106
|
- **秒数是读出来的,不是写死的**:卡片里的秒数就是当前生效的 `sleepCountdownSeconds`;月亮按钮被关掉时,卡片还会补一句「按钮目前关闭,可在设置里打开。」
|
|
104
|
-
- **状态存在哪**:`<DSH_HOME>/storages/dsh-autorestart/state.json`(和插件的检查点同一个文件)。宿主用 `GET /announcements` 报(`{ ok, pending: [{ feature, version }] }`),用 `POST /announcements`(body `{ feature }
|
|
107
|
+
- **状态存在哪**:`<DSH_HOME>/storages/dsh-autorestart/state.json`(和插件的检查点同一个文件)。宿主用 `GET /announcements` 报(`{ ok, pending: [{ feature, version }] }`),用 `POST /announcements`(body `{ feature }`)记。里面的 `version` 就是功能版本,跟 `SLEEP_FEATURE_VERSION` 是同一个数。
|
|
105
108
|
|
|
106
109
|
## 工作方式
|
|
107
110
|
|
|
@@ -134,7 +137,7 @@ agent/status(running) ──► 写 checkpoint(<DSH_HOME>\storages\dsh-autores
|
|
|
134
137
|
|
|
135
138
|
## 安装
|
|
136
139
|
|
|
137
|
-
当前版本:**0.6.
|
|
140
|
+
当前版本:**0.6.2**。
|
|
138
141
|
|
|
139
142
|
```powershell
|
|
140
143
|
# 在 dsh 里(或对 Agent 说):把工作区里的这个包安装为 profile bundle
|
|
@@ -169,7 +172,8 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
169
172
|
| `restartCommand` | `''` | 自定义重启命令,支持 `{pid}` `{ppid}` `{app}` 占位符 |
|
|
170
173
|
| `relaunchDelaySeconds` | `2` | 脱离的 helper 在杀掉应用**之前**等几秒(`ping -n 3`,约 2 秒) |
|
|
171
174
|
| `settleMs` | `2000` | 静默窗口(ms):最后一次会话活动之后,宿主要安静这么久,重启才允许开始 |
|
|
172
|
-
| `restartGraceMs` | `
|
|
175
|
+
| `restartGraceMs` | `0` | **重启前等待**(ms):最后一个会话收工后,再等这么久才真正重启——**下拉框:0 / 5 / 10 / 20 / 30 / 60 秒**,默认 0(不额外等)。重启是日常操作,所以默认不预留 |
|
|
176
|
+
| `queuedGraceMs` | `60000` | **排队消息的上限**(ms):一条没人认领的 inbox 消息最多挡多久的重启;超过就算卡住,不再挡路(并在日志里点名那个会话)。`0` = 没有上限(0.6.2 之前的行为)。**正在跑的会话在任何设置下都照样挡路** |
|
|
173
177
|
| `autoReloadPage` | `true` | `dsh web`:host 重启(bootId 变化)后让页面自动 reload |
|
|
174
178
|
| `blockManualRestart` | `true` | 硬拦截 agent 用 shell 命令手动杀掉/重启 DSH,并引导到 `continuum_restart` |
|
|
175
179
|
| `healthPath` | `/__dsh_autorestart/health` | 健康检查路由(返回 bootId/pid/phase/自检标志) |
|
|
@@ -179,20 +183,71 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
179
183
|
| `maxBackups` | `2` | 保留几份快照;写新的时自动删旧的 |
|
|
180
184
|
| `rollbackTimeoutMs` | `90000` | 看护脚本等新宿主多久(毫秒),超时就回退快照并再拉起一次 |
|
|
181
185
|
| `showSleepButton` | `true` | 收工关机的月亮按钮是否显示在左侧栏最底部、设置按钮旁边 |
|
|
182
|
-
| `sleepCountdownSeconds` | `20` |
|
|
186
|
+
| `sleepCountdownSeconds` | `20` | **关机倒数秒数**:收工关机在真正关机前倒数几秒——**下拉框:0 / 10 / 20 / 30 / 60 秒**,默认 20;`0` 表示**不倒数、立即关机**(仍会先等所有会话收工)。**这段看得见的倒数就是关机前的那段等待** —— 关机没有第二个看不见的预留 |
|
|
183
187
|
| `sleepCommand` | `''` | 收工关机的自定义关机命令;留空 = 自动识别宿主(Windows 用 `cmd.exe /c …`,其它平台用 `/bin/sh -c …`) |
|
|
184
188
|
|
|
185
189
|
### 为什么收工后还要多等几秒
|
|
186
190
|
|
|
187
191
|
**我们只看得到 DSH 的会话。** 这个插件知道的是:某个会话还在不在跑、有没有排队的 inbox 消息、最后一个是什么时候闲下来的。它**看不到别的插件用定时器排队的活** —— 比如记忆插件在会话结束后过几秒才把这次对话写进记忆、比如一次总结、一次 MCP 回调、一次日志落盘。对我们来说这些活是不存在的,所以「所有 agent 都闲了」并不等于「现在真的没有事情在发生」。
|
|
188
192
|
|
|
189
|
-
|
|
193
|
+
**只有一个预留窗口,给重启。** 重启没有倒数环节:会话一收工就该赶紧重启。`restartGraceMs` 是留给别的插件延迟落盘的那一小段等待 —— 也是**唯一**的一个:
|
|
190
194
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
195
|
+
| 配置 | 管什么 | 默认 | 档位 |
|
|
196
|
+
| --- | --- | --- | --- |
|
|
197
|
+
| `restartGraceMs` | **重启**前等待 | `0`(不额外等) | 0 / 5 / 10 / 20 / 30 / 60 秒 |
|
|
198
|
+
| `sleepCountdownSeconds` | **关机**(看得见的)倒数 | `20`(20 秒) | 0 / 10 / 20 / 30 / 60 秒 |
|
|
199
|
+
|
|
200
|
+
- **重启闸门只听 `restartGraceMs`**:它等的是 `max(settleMs, restartGraceMs)` 的安静。默认(0)就是只等静默窗口本身 —— 会话一收工就重启。
|
|
201
|
+
- **收工关机没有看不见的预留**:关机前的那段等待**就是屏幕上的那段倒数** —— 秒数看得见,用户可以盯着它、也可以点「不关了」取消。再加一个看不见的窗口只是把同一件事说两遍,所以没有了:所有会话一收工,倒数立刻开始。
|
|
202
|
+
- **等待期间又有活,重新计时**:预留永远跟着「最后一次活动」走 —— 会话又开始干活,就从那一刻重新等。
|
|
203
|
+
- **在哪改**:设置 → 自动重启与关机,分成两个带标题的框 ——「重启相关设置」(重启前等待 + 两个备份开关)在前,「关机相关设置」(关机倒数秒数)在后。
|
|
204
|
+
- **怎么排障**:状态接口都带着它们 —— `GET /health?state=1`、`GET /health?gate=1`、`continuum_status` 报 `graceMs`(重启预留)和 `effectiveSettleMs`(闸门真正在等的窗口);`GET /sleep` 报 `restartGraceMs` 和 `countdownSeconds`(正在走的倒数);都还带 `quietForMs`(已经安静了多久)。
|
|
205
|
+
|
|
206
|
+
### 为什么「排队的消息」不能永远挡着重启
|
|
207
|
+
|
|
208
|
+
**死锁是怎么来的。** 一个会话的 inbox 里躺着一条**没人认领**的消息时,它在重启闸门眼里就算「忙」。消息马上要被领走时这是对的 —— agent 一两秒就来领了;但**没人会领**的时候就是死锁。真实现场就是:UI 里新开的一个**空会话**(一个轮次都没跑过),inbox 里有条没人认领的消息 → 闸门永远关着 → 硬期限到了也只是记一条告警 → **重启永远不触发**。
|
|
209
|
+
|
|
210
|
+
**上限就是为这件事留的。** `queuedGraceMs`(默认 **60000** 毫秒)是一条没人认领的消息最多能挡多久的重启:
|
|
211
|
+
|
|
212
|
+
- **窗口内被认领 → 照样挡**:正常情况完全没变,agent 把消息领走,重启继续等它。
|
|
213
|
+
- **超过窗口还没人领 → 卡住了**:它**不再挡路**,日志里点名那个占着队列的会话(`the queued message of session <id> has been waiting 61s and nobody claimed it; it no longer blocks the restart`),重启照常往下走。
|
|
214
|
+
- **正在跑的会话在任何设置下都照样挡**:这个上限只管**排队**的活,**在跑的轮次永远不放宽**,也没有任何配置能让插件去杀掉一个正在跑的会话。
|
|
215
|
+
- **计时跟着活走**:会话开始干活 → 排队计时清掉;之后再排队 → 从零重新计。
|
|
216
|
+
- **`0` 就是「和以前一样」**:`queuedGraceMs: 0` 时,没人认领的消息永远挡着重启,和 0.6.2 之前完全一致。
|
|
217
|
+
- **在哪看**:`GET /health?state=1`、`GET /health?gate=1`、`continuum_status` 都会报 `queuedGraceMs`。
|
|
218
|
+
|
|
219
|
+
### 长期等待的时候,用户看到什么
|
|
220
|
+
|
|
221
|
+
硬期限(`deferHardMs`)到了、还有会话在跑的时候,插件**不再装作「一切正常,只是在等一会儿」**,但也**不会放弃**:
|
|
222
|
+
|
|
223
|
+
- **请求留着**:待重启仍然是待重启,请求方的会话仍然记在检查点里,闸门一 quiet 就照常重启。**不会在用户背后把请求悄悄丢掉**(不 cancel、不 releaseRequesters)。
|
|
224
|
+
- **等待变得看得见**:日志每分钟最多一条(不是每 3 秒刷屏)——「已等待 17 分钟,XXX 一直占着;可以取消或等它收工」;`GET /health?state=1` 里多一个 `longWait` 对象(`since` / `reason` / `blockedBy`)。
|
|
225
|
+
- **气泡里也说一句**:待重启的胶囊多显示一行「已等 17 分:XXX 一直占着」(英文:`waiting for 17m: XXX keeps holding it`)。
|
|
226
|
+
|
|
227
|
+
**不会**为了重启去强杀任何会话:`forceRestartWhenBusy` 默认仍然是关的,这个插件也不会去打开它。
|
|
228
|
+
|
|
229
|
+
### 安静窗口到底在量什么
|
|
230
|
+
|
|
231
|
+
重启闸门有两层:**没有会话在忙** + **一段安静窗口**(`max(settleMs, restartGraceMs)`)。
|
|
232
|
+
|
|
233
|
+
安静窗口只回答一个问题:**距上一次「真的有活」过了多久。** 它从下面两个标记里**较晚的那个**开始算:
|
|
234
|
+
|
|
235
|
+
- **最后一次真正的活动信号** —— 一轮开始或结束(`agent/status`)、一条消息被排队(`agent/inbox/inserted`)、或者一个说明「确实在干活」的会话事件(消息、工具调用、轮次);
|
|
236
|
+
- **最后一次看见会话在忙** —— 正在跑的轮次,或者排队的消息(`busyAgents()`)。
|
|
237
|
+
|
|
238
|
+
**例行的会话事件不算活动。** 宿主在界面轮询/状态同步时会持续产生会话事件(标题、投影、请求上下文、已结算的任务),实测每 2~3 秒一个。以前这些都算活动,于是安静窗口每两三秒就被清零 —— **只要窗口比事件间隔长,它就永远凑不满,重启永远不会触发**(这就是 0.6.0 把宽限期从 2 秒抬到 10 秒之后「昨晚正常、今天不动」的原因)。
|
|
239
|
+
|
|
240
|
+
这条规则是**单边**的:**读不出类型的事件一律不算活动** —— 拿不准的时候宁可早重启,也不要永远不重启。
|
|
241
|
+
|
|
242
|
+
**正在跑的会话永远挡路。** 安静窗口只决定「什么时候可以重启」;「有没有会话在忙」是硬保证,任何配置都放松不了它。
|
|
243
|
+
|
|
244
|
+
**重启不动的时候去哪儿看。** 状态接口会把闸门的原因和阻塞者一起报出来(`GET /health?state=1` 里的 `reason`:`conversation-active` = 有会话在忙,`settling` = 只是安静窗口还没凑满,`quiet` = 可以走了);待重启期间日志里每分钟会写一条诊断行:
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
restart gate: action=wait reason=agents-busy-within-soft-window busyCount=1 quietForMs=1432 effectiveSettleMs=10000
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`action` 是这次决策做了什么(`wait` / `restart` / `long-wait` …),`reason` 是为什么,`busyCount` 是几个会话在挡,`quietForMs` 是已经安静了多久,`effectiveSettleMs` 是要凑满的窗口。
|
|
196
251
|
|
|
197
252
|
## Agent 工具与"它怎么知道该用插件"
|
|
198
253
|
|
|
@@ -237,7 +292,7 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
237
292
|
|
|
238
293
|
**倒计时不遮界面。** 倒数是角落里的一张**浮动卡片**,不是全屏遮罩:它不压暗页面、不锁滚动、也不吃掉任何点击——卡片外面照常能读正文、能滚动、能点侧栏切会话,卡片自己**只有那个「算了,别关」按钮能点**。切换会话或切换页面也**不会打断或重置**倒数:秒数来自宿主的轮询,不是来自当前打开的那个视图。
|
|
239
294
|
|
|
240
|
-
**倒数多长由设置分区决定。** `sleepCountdownSeconds` 是一个**下拉框**:0 / 10 / 20 / 30 / 60 秒,默认 20 秒。`0` = **不倒数、立即关机**:所有会话一收工、闸门一开就直接关机(忙着的会话照样挡住它),屏幕上**不会出现倒计时卡片**。配置里还留着下拉框之外的老数值(15、45…)时,会自动吸附到最近的一档(15→20、45→60
|
|
295
|
+
**倒数多长由设置分区决定。** `sleepCountdownSeconds` 是一个**下拉框**:0 / 10 / 20 / 30 / 60 秒,默认 20 秒。`0` = **不倒数、立即关机**:所有会话一收工、闸门一开就直接关机(忙着的会话照样挡住它),屏幕上**不会出现倒计时卡片**。配置里还留着下拉框之外的老数值(15、45…)时,会自动吸附到最近的一档(15→20、45→60),不会把插件弄坏。这个下拉框在**设置 → 自动重启与关机 →「关机相关设置」**里。**这段看得见的倒数就是关机前的那段等待** —— 关机没有第二个看不见的预留。
|
|
241
296
|
|
|
242
297
|
**点一下就够了。** 月亮按钮按下就武装:宿主等所有忙会话收工,在角落里倒数 **20 秒**(浮动卡片,不遮挡界面),然后关掉整台机器。**再点一下**(或点卡片上的「算了,别关」)就取消。除此之外什么都不用用户做:没有确认框、不用点第二次。
|
|
243
298
|
|
|
@@ -252,7 +307,7 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
252
307
|
|
|
253
308
|
**意图存在哪。** 检查点文件(`<DSH_HOME>/storages/dsh-autorestart/state.json`)里的 `sleep: { armed, armedAt, countdownSeconds }`。机器关掉、关机命令没跑起来、或者你点了「不关了」,它都会被清掉。**记录坏掉就直接丢弃** —— 宁可让你再点一次,也不能因为半截文件让机器永远自己关自己。
|
|
254
309
|
|
|
255
|
-
**排障用。** `GET /sleep` 会报 `armedAt`(什么时候点的关机),旁边还有 `phase`、`blockers`、`
|
|
310
|
+
**排障用。** `GET /sleep` 会报 `armedAt`(什么时候点的关机),旁边还有 `phase`、`blockers`、`countdownSeconds`(正在走的倒数)和 `restartGraceMs`(重启前等待)。
|
|
256
311
|
|
|
257
312
|
### 故障排查:点了没关机
|
|
258
313
|
|
|
@@ -275,7 +330,7 @@ GET /__dsh_autorestart/sleep
|
|
|
275
330
|
|
|
276
331
|
1. **还有任务在跑。** `blockers[]` 列的就是它们,看 `kind`:`session` 是会话,`subagent` 是某个会话派出去的子智能体。**子智能体也算在干活,这是故意的** —— 否则重启会把它掐断。**怎么办:** 等它干完,或者把那个会话停掉;列表会自己变短。
|
|
277
332
|
2. **有待执行的重启。** `kind` 是 `restart-pending`(`label`/`name` 都是 `待执行的重启`)。这是正常排队,不是故障:先重启,会话跟着回来,完事之后倒数自动接着走。**怎么办:** 不用管,等重启完即可(也可以用 `continuum_cancel_restart` 把这次重启取消掉)。
|
|
278
|
-
3.
|
|
333
|
+
3. **刚收工,倒数还没开始。** 关机**没有看不见的预留**:所有会话一收工,倒数立刻开始。**你会看到:** `blockers[]` 空了、`phase` 还是 `waiting`,说明会话刚刚才闲下来,这一轮 tick 还没把机器切到 `countdown`。**怎么办:** 不用管,下一拍就进倒数了;真要有落盘的活要等,把「关机倒数秒数」调大就行。(重启有它自己的窗口 —— `restartGraceMs`,默认不预留 —— 两个互不影响。)
|
|
279
334
|
4. **倒数期间又有会话开始干活。** 倒数退回「等待」,`remainingMs` 变回满值。**你会看到:** 消息里说又有谁开始干活了,`blockers[]` 列着是谁。**怎么办:** 等他们干完。
|
|
280
335
|
5. **想看「到底等谁」。** `continuum_status` 的 `blockedBy[]` 和 `GET /sleep` 的 `blockers[]` 是**同一份数据**(前者给 agent 看,后者给脚本看)。每条都有 `kind`(它是什么)、`label`(会话标题)、`name`(完整称呼,比如「写代码的会话的子智能体」)。标题解析不到时 `label` 是 `null`、`name` 是空字符串 —— 这时候等待那句话就只说「等 3 个任务收工」,不带名字列表。
|
|
281
336
|
6. **彻底不想关了。** 等待卡片上的取消按钮(或者再点一次月亮按钮,或者 `POST /sleep {"action":"cancel"}`)会把这个意图彻底清掉:`armed` 变回 `false`,`armedAt` 变回 `null`。
|
|
@@ -308,17 +363,25 @@ WSL 的判定依据:`WSL_DISTRO_NAME`、`WSL_INTEROP`、`/proc/version` 里出
|
|
|
308
363
|
| 键 | 默认 | 说明 |
|
|
309
364
|
| --- | --- | --- |
|
|
310
365
|
| `showSleepButton` | `true` | 左侧栏最底部(设置按钮旁边)的月亮按钮是否显示 |
|
|
311
|
-
| `sleepCountdownSeconds` | `20` | 真正关机前倒数几秒——**下拉框:0 / 10 / 20 / 30 / 60 秒**,默认 20;`0` =
|
|
366
|
+
| `sleepCountdownSeconds` | `20` | 真正关机前倒数几秒——**下拉框:0 / 10 / 20 / 30 / 60 秒**,默认 20;`0` = 不倒数、立即关机(仍会先等会话收工)。**这段看得见的倒数就是关机前的那段等待** |
|
|
312
367
|
| `sleepCommand` | `''` | 自定义关机命令;留空 = 自动识别宿主,填了就用这条命令代替(Windows 走 `cmd.exe /c …`,其它平台走 `/bin/sh -c …`) |
|
|
313
368
|
|
|
314
|
-
**设置分区(设置 →
|
|
369
|
+
**设置分区(设置 → 自动重启与关机)分成两个带标题的框。** 这个分区是设置页侧栏里的**独立一栏**(排在「Agent预设」下面),点进去直接就是这两个框——不用先去插件详情页,也不用再展开任何一层。**「重启相关设置」在前,「关机相关设置」在后。**
|
|
370
|
+
|
|
371
|
+
**「重启相关设置」**
|
|
315
372
|
|
|
316
373
|
| 控件 | 配置键 | 说明 |
|
|
317
374
|
| --- | --- | --- |
|
|
318
|
-
|
|
|
375
|
+
| 重启前等待(下拉) | `restartGraceMs` | 0 / 5 / 10 / 20 / 30 / 60 秒,默认 0(不额外等) |
|
|
319
376
|
| 重启前备份(开关) | `backupOnRestart` | 关掉就完全不做重启前快照 |
|
|
320
377
|
| 备份包含已安装插件(开关) | `backupIncludePlugins` | 关掉后只备份 DSH 自身状态,不含 profile 里的插件目录;**「重启前备份」关掉时它置灰** |
|
|
321
378
|
|
|
379
|
+
**「关机相关设置」**
|
|
380
|
+
|
|
381
|
+
| 控件 | 配置键 | 说明 |
|
|
382
|
+
| --- | --- | --- |
|
|
383
|
+
| 关机倒数秒数(下拉) | `sleepCountdownSeconds` | 0 / 10 / 20 / 30 / 60 秒;`0` = 不倒数、立即关机 |
|
|
384
|
+
|
|
322
385
|
改完立刻写进 profile 配置(走插件自己的 choice 路由,用官方 `configEditor` Service),并且**从宿主回读**,所以分区里永远不会出现宿主没接受的值。**非法值一律 400、一个字节都不写。**
|
|
323
386
|
|
|
324
387
|
**首次安装会问一次「按钮要不要留着」。** 答完写进 profile 配置的 `showSleepButton` 键(用官方 `configEditor` Service,和备份范围那次问询同一条路),所以设置分区和问询看到的永远是同一个值。
|
|
@@ -515,6 +578,7 @@ lib/checkpoint.js 原子 JSON 检查点,容错读取,boot/restart/session
|
|
|
515
578
|
lib/lifecycle.js 纯决策:bootPlan / restartDecision / selectGoalDisarms / continuationPrompt / describeState
|
|
516
579
|
lib/restart.js Windows 脱离重启(WMI 主、schtasks 兜底、dryRun、自定义命令)
|
|
517
580
|
lib/sleep.js 收工关机:识别真宿主机(含 WSL)、关机命令、等待→倒数→关机的状态机
|
|
581
|
+
lib/semver.js 版本号比较(公告去重用:只看"记录版本是否低于当前功能版本")
|
|
518
582
|
locale/{en,zh}.json Plugin Manager 显示用标题/描述
|
|
519
583
|
icon.svg 插件图标
|
|
520
584
|
LICENSE MIT 许可(发布到插件市场需要)
|
|
@@ -528,12 +592,20 @@ A restart is never scheduled while another DSH conversation is still working.
|
|
|
528
592
|
|
|
529
593
|
1. Conversation gate: busyAgents() inspects every live agent (subagents and
|
|
530
594
|
teammates included). An agent blocks the restart when its status is 'running'
|
|
531
|
-
or when it still has queued inbox work
|
|
532
|
-
inbox.hasPending)
|
|
595
|
+
(ALWAYS, under every setting) or when it still has queued inbox work
|
|
596
|
+
(inbox.nextTurn / inbox.nextStep, or inbox.hasPending) — the queued work only
|
|
597
|
+
blocks until queuedGraceMs (default 60000 ms) is over; after that it is
|
|
598
|
+
treated as stuck, it stops blocking and the session that holds it is named in
|
|
599
|
+
the log. The verdict is { allow:false, reason:'conversation-active',
|
|
533
600
|
blockedBy:[...] }.
|
|
534
601
|
2. Settle gate: even with no busy conversation, the host must have been quiet for
|
|
535
|
-
settleMs (default 2000 ms) since the last
|
|
536
|
-
|
|
602
|
+
settleMs (default 2000 ms) since the last REAL activity — a turn that started
|
|
603
|
+
or ended (agent/status), a message that was queued (agent/inbox/inserted), a
|
|
604
|
+
session event that says work happened (a message, a tool, a turn), or the last
|
|
605
|
+
moment a conversation was seen working. A ROUTINE session event (a title, a
|
|
606
|
+
projection, a context request) is not activity: the host emits those every
|
|
607
|
+
two to three seconds, so counting them kept the window from ever filling and
|
|
608
|
+
the restart never fired. Otherwise the verdict is
|
|
537
609
|
{ allow:false, reason:'settling' }. This closes the gap between the decision
|
|
538
610
|
and the detached helper's ping delay, where a new turn could otherwise start
|
|
539
611
|
and be killed.
|