# dsh-auto-continue 一个给 [DeepSeek Harness(`dsh`)](https://github.com/deepseek-ai/deepseek-harness) 用的**双半插件**(host + 浏览器),在对话输入栏加两个开关: 1. **自动续跑** —— 请求因 `RATE_LIMIT` / `QUOTA` 中断时,额度/速率限制一重置就自动接着跑。 2. **闲时任务** —— 峰价时段自动**挂起**正在跑的会话,进入谷价时段自动**恢复**。长任务不用守着,等便宜时段自己跑。 两个开关都在输入栏按钮上,下方状态行显示当前峰/谷价与距下次切换的时间。开关状态经 dsh settings 服务持久化,重启后仍在。 [English README](./README.md) ## 安装 ```bash dsh plugin --profile web add github:Meowrium/dsh-auto-continue ``` 然后重启 `dsh web`。本插件是 bundle,自带 patch 层,**不需要手改 `cordis.patch.yml`**。 > 需要 `dsh` ≥ `0.1.5-alpha.1`(实测于 `0.1.5-rc.1`)。见下方[兼容性](#兼容性)。 ## 用法 重启后打开会话,输入栏会出现两个胶囊按钮: | 按钮 | 作用 | | --- | --- | | **闲时任务** | 峰价时段:挂起正在运行的会话;谷价时段:自动恢复 | | **自动续跑** | 命中 `RATE_LIMIT` / `QUOTA` 后,按重置时间自动重试 | 任一开启后,输入框下方会多出一行状态,例如 `谷价 · 距峰价 23分 · 自动续跑`。 ## 工作原理 ### 峰谷时段 全部为**北京时间(UTC+8,无夏令时)**。峰价为: ``` 09:00 – 12:00 以及 14:00 – 18:00 ``` 其余时间为谷价。 - **进入峰价** —— 每个*正在运行*的 agent 以 `{ keepInbox: true }` 取消(消息不丢),并在 `agent/pre-step` 拦截新一轮:把已认领的消息塞回 agent 的 inbox,然后拒绝该 step。 - **进入谷价** —— 给每个被挂起的 agent 发一次 `followup`,让它接着干。 - 把闲时任务开关**关掉**会立刻恢复所有被挂起的会话。 时段边界是 `lib/index.js` 里的一个常量: ```js const BOUNDARIES_SEC = [9 * 3600, 12 * 3600, 14 * 3600, 18 * 3600]; ``` 按你自己的价格表改这四个值即可。 ### 自动续跑 挂 `agent/request-error`。若失败码是 `RATE_LIMIT` 或 `QUOTA`,用 provider 给的 `Retry-After`(`providerRetryAfterMs`)安排定时恢复;缺失时兜底 **60 秒**,上限 **24 小时**。当前请求正常结束,之后会话自己继续。 ### 设置项 持久化在 dsh settings 命名空间 **`auto-continue`**: | 键 | 类型 | 默认 | | --- | --- | --- | | `idleTaskEnabled` | boolean | `false` | | `autoContinueEnabled` | boolean | `false` | ## 包结构 单包同时承载插件的两个半: ``` package.json dsh.bundle.patch → cordis.patch.yml(插入 host 半) dsh.client → lib/client.js (浏览器半) cordis.patch.yml bundle patch:插入条目 { id: auto-continue, name: dsh-auto-continue } lib/index.js host 半 —— settings、定时器、agent 事件 lib/client.js 浏览器半 —— 输入栏开关 + 状态行 ``` ## 兼容性 - **不 import** `@deepseek-ai/dsh-settings` 的任何具名导出。`settingsNamespace` / `installSettingsSection` 在 dsh `0.1.2-alpha.1` 已被删除,而缺具名导出会在模块链接期直接抛 `SyntaxError`,整个插件都活不了。改为经 `ctx.inject(["settings"])` 拿服务、调用其 `installSection(...)` —— 与官方 `dsh-agent-default-model` 的做法一致。 - 浏览器半用 `slots.inject(...)` 注册进 `conversation.input.left` 与 `conversation.composer.dock`;dsh 0.1.5 起这是必需写法(直接 `register` 会抛 "slot ... is not declared")。 - 浏览器模块 id 与包名一致(`dsh-auto-continue`),因为 `dsh-client-modules` 以 loader entry 名作为客户端模块 id。 ## 许可 [MIT](./LICENSE)