# dsh-auto-continue

> 为 DSH Web UI 自动续跑因网络等原因中断的请求，遇到认证失败等永久性错误会跳过并通知。

## Metadata

- Author: [@HsiangNianian](https://github.com/HsiangNianian)
- Repo: <https://github.com/HsiangNianian/dsh-auto-continue.git>
- GitHub: [HsiangNianian/dsh-auto-continue](https://github.com/HsiangNianian/dsh-auto-continue)
- Stars: 34
- Language: TypeScript
- License: [MIT](https://spdx.org/licenses/MIT.html)
- Topics: `auto-continue`, `automatic`, `cordis`, `deepseek-harness`, `dsh`, `dsh-plugin`, `plugin`, `web-ui`
- Forks: 3
- Open Issues: 0
- Last push: 2026-08-21T03:09:11.000Z
- Added: 2026-08-14T00:00:00.000Z

## Install

```bash
dsh plugin --profile web add github:HsiangNianian/dsh-auto-continue
```

## Wiki

## 一句话定位
为 DSH Web UI 在请求被网络中断时自动续跑的插件——把"继续"模拟成普通用户消息自动发送，遇到永久性错误会跳过并通知。

## 核心能力
- 检测回合结束事件，对临时性失败（网络/超时/5xx/429 等）和 token 超限自动发送"继续"消息
- 自适应退避：连续失败时冷却时间按倍数递增（默认 20s → 40s → 80s…），避免对故障上游狂发请求
- 幂等护栏：续跑前检查上一步工具调用状态，若工具结果未确认或已完成，给模型加上一段提示，避免重复执行
- 循环守卫：识别运行中回合的空转（连续短句、同工具反复调用、相同消息重复），自动取消并用循环提示文本重启回合
- 启动/重连扫描：浏览器关闭期间若宿主崩溃遗留中断的回合，恢复后会扫描并自动续跑
- 设置卡片统计今日自动继续次数、跳过数、恢复数、失败数、达上限停止、循环打断，可按错误码分类并一键清零
- 可选浏览器通知：自动继续/放弃/永久性错误时弹窗，可直接点"立即续跑"或"暂停该会话 1 小时"

## 技术实现
- **语言**: TypeScript
- **关键依赖**: @deepseek-ai/cordis、@deepseek-ai/schemastery、@deepseek-ai/dsh-settings、@deepseek-ai/dsh-client-runtime
- **架构模式**: 宿主-浏览器双半插件；宿主侧单实例引擎订阅会话事件流（`session/event`），通过 `agent.followup` 发送、`agent.cancel` 取消；浏览器侧瘦壳只提供设置卡片和 SSE 状态桥
- **入口文件**: src/index.ts（宿主侧）/ src/client/index.ts（浏览器侧）

## 适用场景
使用 DSH Web 跑长时间任务时，网络抖动或服务端瞬时 5xx 会让回合中断，常见做法是手动点重发。本插件免去这个动作——只要网络或服务端是临时性故障，等几秒就会自动接着跑。也适合浏览器关闭后回来发现任务半途而废的场景，开页即恢复。

## 前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH | ≥ 0.1.0-rc.7 | 低于 rc.7 时设置卡片加载失败（`list slot ... requires options.id`），必须升级 DSH |
| Node.js | ≥ 18 | 仅在从源码手动构建时要求；通过 `dsh plugin add` 安装预构建包不要求 |
| 平台 | 跨平台 | 插件无原生模块依赖，无平台限制 |
| 原生模块 | 无 | 引擎基于宿主进程内的内存状态，无 node-pty / sqlite 等原生依赖 |

## 安装方式
```bash
dsh plugin --profile web add github:HsiangNianian/dsh-auto-continue
```

## 配置项
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| paused | 布尔 | 全局暂停：实时与扫描都不再自动发送，已排队的待发送也会取消 | 关 |
| continueText | 文本 | 中断后自动发送的消息内容，支持 `{code}` `{tool}` `{message}` 等占位符 | `继续` |
| continueTextMaxTokens | 文本 | 达到输出 token 上限时自动发送的文本，支持相同占位符 | `继续` |
| guardTools | 布尔 | 续跑前检查上一步工具调用并加护栏提示，避免重复执行 | 开 |
| guardPendingText | 文本 | 工具结果未确认时附加的提示文本，支持 `{tool}` 占位符 | `(上一步工具「{tool}」可能未完成, 先确认状态再继续, 不要重复执行)` |
| guardDoneText | 文本 | 工具已成功时附加的提示文本，支持 `{tool}` `{result}` 占位符 | `(上一步工具「{tool}」已完成, 结果: {result}; 不要重复执行, 直接继续)` |
| graceMs | 数字 | 中断后等待宿主自行恢复的宽限期，期间恢复则取消自动发送（毫秒） | `3000` |
| cooldownMs | 数字 | 同一会话两次自动"继续"的最小间隔，失败尝试也计入（毫秒） | `20000` |
| maxConsecutive | 数字 | 同一会话连续自动"继续"的上限；超过后停止，等用户介入或成功回合 | `3` |
| scanOnBoot | 布尔 | 页面启动或重连时扫描最近中断的会话并自动续跑（如浏览器关闭期间宿主崩溃） | 开 |
| scanLimit | 数字 | 启动扫描最多检查多少个最近更新的会话（不含运行中和子代理会话） | `8` |
| freshMs | 数字 | 扫描只处理该时间窗内的中断（毫秒） | `900000`（15 分钟） |
| classify | 布尔 | 错误分类：仅自动恢复临时性错误；认证/余额/模型不存在等永久性错误跳过并通知 | 开 |
| backoffFactor | 数字 | 连续失败时冷却间隔的倍率 | `2` |
| backoffMaxMs | 数字 | 自适应退避上限，防止等待过久（毫秒） | `300000`（5 分钟） |
| notify | 布尔 | 自动继续/放弃/永久性错误时弹浏览器通知（首次会请求权限） | 关 |
| loopGuard | 布尔 | 检测运行中的回合空转并自动取消重启 | 开 |
| loopShortChars | 数字 | 模型消息短于该字符数视为一条短句（空转信号） | `40` |
| loopWindowMs | 数字 | 连续短句必须落在该时间窗内才计入空转（毫秒） | `30000` |
| loopShortCount | 数字 | 时间窗内连续多少条短句且无工具调用时判定空转循环 | `12` |
| loopRepeatText | 数字 | 连续多少条完全相同的消息时判定空转循环（不限长度，最强信号） | `4` |
| loopToolRepeat | 数字 | 同工具+同参数+同结果连续调用多少次时判定死循环 | `5` |
| loopText | 文本 | 循环守卫打断后重启回合时发送的文本，支持 `{tool}` 占位符 | `(检测到你可能陷入循环, 请停止重复刚才的动作, 换一种方式继续)` |
| verbose | 布尔 | 在浏览器控制台输出 `[auto-continue]` 日志 | 开 |

## 常见问题
**Q: 哪些中断会触发自动续跑，哪些不会？**

A: 临时性失败（网络/超时/5xx/429）会触发；达到输出 token 上限也会。用户主动停止（`aborted`）、策略拒绝（`blocked`）以及永久性错误（401/403 状态码，或匹配认证/凭据/余额/配额/模型不存在/上下文超限等关键词）一律不续跑，而是跳过并通知。

**Q: 多个浏览器标签页同时打开会重复发送吗？**

A: 不会。自 0.8.0 起引擎跑在宿主进程内（`src/host/engine.ts`），全局只有一个实例，多标签同时打开也不会重复发送；浏览器标签全部关掉引擎仍在值守。

**Q: 需要重启 `dsh web` 才能生效吗？**

A: 安装后首次启用需要重启 `dsh web`。之后所有改动通过设置卡片即时保存（写入 `~/.dsh/settings.yaml`），保存后立即生效，已打开的页面也会刷新；如遇个别字段不生效，重启 `dsh web` 即可。

**Q: 怎样手动关闭自动继续？**

A: 打开"设置 → 插件"，找到"自动继续"卡片，把全局"暂停自动继续"开关打开即可实时取消一切自动发送。需要只暂停某一个会话时，可点浏览器通知里的"暂停该会话 1 小时"按钮，或在卡片底部"已暂停会话"列表里逐个解除。

**Q: 卸载插件后留在 `~/.dsh/settings.yaml` 的段落需要清理吗？**

A: 可以不清理。卸载插件时该段落保留在配置文件里无害；想彻底清理时手动删除 `auto-continue` 段落即可。

**Q: 循环守卫会不会误判？会把我手动停止的回合重启吗？**

A: 不会误判你手动停止。循环守卫发起的取消带有内部来源标记，引擎只对守卫取消触发自动重启，用户手动停止不会触发任何动作。阈值、时间窗、提示文本均可调，遇到误判可放宽 `loopShortCount` / `loopWindowMs`。

**Q: 提示文本里的占位符怎么写？**

A: `continueText` 与 `continueTextMaxTokens` 支持 `{code}` `{message}` `{status}` `{tool}` `{turn}` `{errorCount}` `{sessionTitle}` `{elapsed}`。护栏文本（`guardPendingText` / `guardDoneText` / `loopText`）支持 `{tool}` 与 `{result}`（结果摘要）。例如 `继续 ({tool}: {code})` 会渲染成 `继续 (git push: UPSTREAM)`。

## 上手难度
入门 — 安装一行命令，默认配置即可工作，需要调整时打开设置卡片对照说明修改，无需写代码。

## 已知问题与限制
- `reconnectScanDelayMs`（重连扫描延迟，默认 5000ms）与 `reconnectBackoffMs`（重连退避，默认 3000ms）为遗留字段：引擎自 0.8.0 迁入宿主后不再使用，仅为兼容旧配置保留；改动这两个值不会生效。
- 在 DSH < 0.1.0-rc.7 上安装会因 `settings.plugin.item` slot 不支持 keyed 注册而加载失败，需要先升级 DSH。
- `isTransientFailure` 通过正则匹配错误关键词（auth、credential、api_key、balance、quota、context length 等），若上游错误信息使用不常见的措辞且未命中 401/403，可能被误归类为临时性错误触发无意义的重试——可通过关闭 `classify` 改为全量续跑。
- 浏览器通知依赖浏览器 Notification API 与用户授权，被拒绝后不会再次请求。

---

This document is auto-generated by [deepseek-plugin.org](https://deepseek-plugin.org). HTML page: [dsh-auto-continue](https://deepseek-plugin.org/plugins/HsiangNianian/dsh-auto-continue)
Wiki generated by AI (model: `MiniMax-M3`)
