dsh-autorestart 0.6.1 → 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 +129 -30
- package/client.js +805 -133
- package/cordis.patch.yml +18 -8
- package/docs/README.zh.md +87 -19
- package/index.js +381 -59
- package/lib/activity.js +124 -0
- package/lib/lifecycle.js +17 -4
- package/lib/queued.js +146 -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
|
@@ -137,7 +137,7 @@ agent/status(running) ──► 写 checkpoint(<DSH_HOME>\storages\dsh-autores
|
|
|
137
137
|
|
|
138
138
|
## 安装
|
|
139
139
|
|
|
140
|
-
当前版本:**0.6.
|
|
140
|
+
当前版本:**0.6.2**。
|
|
141
141
|
|
|
142
142
|
```powershell
|
|
143
143
|
# 在 dsh 里(或对 Agent 说):把工作区里的这个包安装为 profile bundle
|
|
@@ -172,7 +172,8 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
172
172
|
| `restartCommand` | `''` | 自定义重启命令,支持 `{pid}` `{ppid}` `{app}` 占位符 |
|
|
173
173
|
| `relaunchDelaySeconds` | `2` | 脱离的 helper 在杀掉应用**之前**等几秒(`ping -n 3`,约 2 秒) |
|
|
174
174
|
| `settleMs` | `2000` | 静默窗口(ms):最后一次会话活动之后,宿主要安静这么久,重启才允许开始 |
|
|
175
|
-
| `restartGraceMs` | `
|
|
175
|
+
| `restartGraceMs` | `0` | **重启前等待**(ms):最后一个会话收工后,再等这么久才真正重启——**下拉框:0 / 5 / 10 / 20 / 30 / 60 秒**,默认 0(不额外等)。重启是日常操作,所以默认不预留 |
|
|
176
|
+
| `queuedGraceMs` | `60000` | **排队消息的上限**(ms):一条没人认领的 inbox 消息最多挡多久的重启;超过就算卡住,不再挡路(并在日志里点名那个会话)。`0` = 没有上限(0.6.2 之前的行为)。**正在跑的会话在任何设置下都照样挡路** |
|
|
176
177
|
| `autoReloadPage` | `true` | `dsh web`:host 重启(bootId 变化)后让页面自动 reload |
|
|
177
178
|
| `blockManualRestart` | `true` | 硬拦截 agent 用 shell 命令手动杀掉/重启 DSH,并引导到 `continuum_restart` |
|
|
178
179
|
| `healthPath` | `/__dsh_autorestart/health` | 健康检查路由(返回 bootId/pid/phase/自检标志) |
|
|
@@ -182,20 +183,71 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
182
183
|
| `maxBackups` | `2` | 保留几份快照;写新的时自动删旧的 |
|
|
183
184
|
| `rollbackTimeoutMs` | `90000` | 看护脚本等新宿主多久(毫秒),超时就回退快照并再拉起一次 |
|
|
184
185
|
| `showSleepButton` | `true` | 收工关机的月亮按钮是否显示在左侧栏最底部、设置按钮旁边 |
|
|
185
|
-
| `sleepCountdownSeconds` | `20` |
|
|
186
|
+
| `sleepCountdownSeconds` | `20` | **关机倒数秒数**:收工关机在真正关机前倒数几秒——**下拉框:0 / 10 / 20 / 30 / 60 秒**,默认 20;`0` 表示**不倒数、立即关机**(仍会先等所有会话收工)。**这段看得见的倒数就是关机前的那段等待** —— 关机没有第二个看不见的预留 |
|
|
186
187
|
| `sleepCommand` | `''` | 收工关机的自定义关机命令;留空 = 自动识别宿主(Windows 用 `cmd.exe /c …`,其它平台用 `/bin/sh -c …`) |
|
|
187
188
|
|
|
188
189
|
### 为什么收工后还要多等几秒
|
|
189
190
|
|
|
190
191
|
**我们只看得到 DSH 的会话。** 这个插件知道的是:某个会话还在不在跑、有没有排队的 inbox 消息、最后一个是什么时候闲下来的。它**看不到别的插件用定时器排队的活** —— 比如记忆插件在会话结束后过几秒才把这次对话写进记忆、比如一次总结、一次 MCP 回调、一次日志落盘。对我们来说这些活是不存在的,所以「所有 agent 都闲了」并不等于「现在真的没有事情在发生」。
|
|
191
192
|
|
|
192
|
-
|
|
193
|
+
**只有一个预留窗口,给重启。** 重启没有倒数环节:会话一收工就该赶紧重启。`restartGraceMs` 是留给别的插件延迟落盘的那一小段等待 —— 也是**唯一**的一个:
|
|
193
194
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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` 是要凑满的窗口。
|
|
199
251
|
|
|
200
252
|
## Agent 工具与"它怎么知道该用插件"
|
|
201
253
|
|
|
@@ -240,7 +292,7 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
240
292
|
|
|
241
293
|
**倒计时不遮界面。** 倒数是角落里的一张**浮动卡片**,不是全屏遮罩:它不压暗页面、不锁滚动、也不吃掉任何点击——卡片外面照常能读正文、能滚动、能点侧栏切会话,卡片自己**只有那个「算了,别关」按钮能点**。切换会话或切换页面也**不会打断或重置**倒数:秒数来自宿主的轮询,不是来自当前打开的那个视图。
|
|
242
294
|
|
|
243
|
-
**倒数多长由设置分区决定。** `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),不会把插件弄坏。这个下拉框在**设置 → 自动重启与关机 →「关机相关设置」**里。**这段看得见的倒数就是关机前的那段等待** —— 关机没有第二个看不见的预留。
|
|
244
296
|
|
|
245
297
|
**点一下就够了。** 月亮按钮按下就武装:宿主等所有忙会话收工,在角落里倒数 **20 秒**(浮动卡片,不遮挡界面),然后关掉整台机器。**再点一下**(或点卡片上的「算了,别关」)就取消。除此之外什么都不用用户做:没有确认框、不用点第二次。
|
|
246
298
|
|
|
@@ -255,7 +307,7 @@ dsh plugin --profile desktop add C:\path\to\dsh-autorestart
|
|
|
255
307
|
|
|
256
308
|
**意图存在哪。** 检查点文件(`<DSH_HOME>/storages/dsh-autorestart/state.json`)里的 `sleep: { armed, armedAt, countdownSeconds }`。机器关掉、关机命令没跑起来、或者你点了「不关了」,它都会被清掉。**记录坏掉就直接丢弃** —— 宁可让你再点一次,也不能因为半截文件让机器永远自己关自己。
|
|
257
309
|
|
|
258
|
-
**排障用。** `GET /sleep` 会报 `armedAt`(什么时候点的关机),旁边还有 `phase`、`blockers`、`
|
|
310
|
+
**排障用。** `GET /sleep` 会报 `armedAt`(什么时候点的关机),旁边还有 `phase`、`blockers`、`countdownSeconds`(正在走的倒数)和 `restartGraceMs`(重启前等待)。
|
|
259
311
|
|
|
260
312
|
### 故障排查:点了没关机
|
|
261
313
|
|
|
@@ -278,7 +330,7 @@ GET /__dsh_autorestart/sleep
|
|
|
278
330
|
|
|
279
331
|
1. **还有任务在跑。** `blockers[]` 列的就是它们,看 `kind`:`session` 是会话,`subagent` 是某个会话派出去的子智能体。**子智能体也算在干活,这是故意的** —— 否则重启会把它掐断。**怎么办:** 等它干完,或者把那个会话停掉;列表会自己变短。
|
|
280
332
|
2. **有待执行的重启。** `kind` 是 `restart-pending`(`label`/`name` 都是 `待执行的重启`)。这是正常排队,不是故障:先重启,会话跟着回来,完事之后倒数自动接着走。**怎么办:** 不用管,等重启完即可(也可以用 `continuum_cancel_restart` 把这次重启取消掉)。
|
|
281
|
-
3.
|
|
333
|
+
3. **刚收工,倒数还没开始。** 关机**没有看不见的预留**:所有会话一收工,倒数立刻开始。**你会看到:** `blockers[]` 空了、`phase` 还是 `waiting`,说明会话刚刚才闲下来,这一轮 tick 还没把机器切到 `countdown`。**怎么办:** 不用管,下一拍就进倒数了;真要有落盘的活要等,把「关机倒数秒数」调大就行。(重启有它自己的窗口 —— `restartGraceMs`,默认不预留 —— 两个互不影响。)
|
|
282
334
|
4. **倒数期间又有会话开始干活。** 倒数退回「等待」,`remainingMs` 变回满值。**你会看到:** 消息里说又有谁开始干活了,`blockers[]` 列着是谁。**怎么办:** 等他们干完。
|
|
283
335
|
5. **想看「到底等谁」。** `continuum_status` 的 `blockedBy[]` 和 `GET /sleep` 的 `blockers[]` 是**同一份数据**(前者给 agent 看,后者给脚本看)。每条都有 `kind`(它是什么)、`label`(会话标题)、`name`(完整称呼,比如「写代码的会话的子智能体」)。标题解析不到时 `label` 是 `null`、`name` 是空字符串 —— 这时候等待那句话就只说「等 3 个任务收工」,不带名字列表。
|
|
284
336
|
6. **彻底不想关了。** 等待卡片上的取消按钮(或者再点一次月亮按钮,或者 `POST /sleep {"action":"cancel"}`)会把这个意图彻底清掉:`armed` 变回 `false`,`armedAt` 变回 `null`。
|
|
@@ -311,17 +363,25 @@ WSL 的判定依据:`WSL_DISTRO_NAME`、`WSL_INTEROP`、`/proc/version` 里出
|
|
|
311
363
|
| 键 | 默认 | 说明 |
|
|
312
364
|
| --- | --- | --- |
|
|
313
365
|
| `showSleepButton` | `true` | 左侧栏最底部(设置按钮旁边)的月亮按钮是否显示 |
|
|
314
|
-
| `sleepCountdownSeconds` | `20` | 真正关机前倒数几秒——**下拉框:0 / 10 / 20 / 30 / 60 秒**,默认 20;`0` =
|
|
366
|
+
| `sleepCountdownSeconds` | `20` | 真正关机前倒数几秒——**下拉框:0 / 10 / 20 / 30 / 60 秒**,默认 20;`0` = 不倒数、立即关机(仍会先等会话收工)。**这段看得见的倒数就是关机前的那段等待** |
|
|
315
367
|
| `sleepCommand` | `''` | 自定义关机命令;留空 = 自动识别宿主,填了就用这条命令代替(Windows 走 `cmd.exe /c …`,其它平台走 `/bin/sh -c …`) |
|
|
316
368
|
|
|
317
|
-
**设置分区(设置 →
|
|
369
|
+
**设置分区(设置 → 自动重启与关机)分成两个带标题的框。** 这个分区是设置页侧栏里的**独立一栏**(排在「Agent预设」下面),点进去直接就是这两个框——不用先去插件详情页,也不用再展开任何一层。**「重启相关设置」在前,「关机相关设置」在后。**
|
|
370
|
+
|
|
371
|
+
**「重启相关设置」**
|
|
318
372
|
|
|
319
373
|
| 控件 | 配置键 | 说明 |
|
|
320
374
|
| --- | --- | --- |
|
|
321
|
-
|
|
|
375
|
+
| 重启前等待(下拉) | `restartGraceMs` | 0 / 5 / 10 / 20 / 30 / 60 秒,默认 0(不额外等) |
|
|
322
376
|
| 重启前备份(开关) | `backupOnRestart` | 关掉就完全不做重启前快照 |
|
|
323
377
|
| 备份包含已安装插件(开关) | `backupIncludePlugins` | 关掉后只备份 DSH 自身状态,不含 profile 里的插件目录;**「重启前备份」关掉时它置灰** |
|
|
324
378
|
|
|
379
|
+
**「关机相关设置」**
|
|
380
|
+
|
|
381
|
+
| 控件 | 配置键 | 说明 |
|
|
382
|
+
| --- | --- | --- |
|
|
383
|
+
| 关机倒数秒数(下拉) | `sleepCountdownSeconds` | 0 / 10 / 20 / 30 / 60 秒;`0` = 不倒数、立即关机 |
|
|
384
|
+
|
|
325
385
|
改完立刻写进 profile 配置(走插件自己的 choice 路由,用官方 `configEditor` Service),并且**从宿主回读**,所以分区里永远不会出现宿主没接受的值。**非法值一律 400、一个字节都不写。**
|
|
326
386
|
|
|
327
387
|
**首次安装会问一次「按钮要不要留着」。** 答完写进 profile 配置的 `showSleepButton` 键(用官方 `configEditor` Service,和备份范围那次问询同一条路),所以设置分区和问询看到的永远是同一个值。
|
|
@@ -532,12 +592,20 @@ A restart is never scheduled while another DSH conversation is still working.
|
|
|
532
592
|
|
|
533
593
|
1. Conversation gate: busyAgents() inspects every live agent (subagents and
|
|
534
594
|
teammates included). An agent blocks the restart when its status is 'running'
|
|
535
|
-
or when it still has queued inbox work
|
|
536
|
-
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',
|
|
537
600
|
blockedBy:[...] }.
|
|
538
601
|
2. Settle gate: even with no busy conversation, the host must have been quiet for
|
|
539
|
-
settleMs (default 2000 ms) since the last
|
|
540
|
-
|
|
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
|
|
541
609
|
{ allow:false, reason:'settling' }. This closes the gap between the decision
|
|
542
610
|
and the detached helper's ping delay, where a new turn could otherwise start
|
|
543
611
|
and be killed.
|