dsh-auto-continue
Manifest validhehe, I'm definitely going to use DeepSeek harness - dsh-auto-continue: automatically firing one more shot to recover from three types of interruptions - max-tokens truncation, 429 rate limiting, and context compression swallowing turns.
dsh-auto-continue
嘻嘻,我一定要用 DeepSeek harness
DSH 回合被 max-tokens 输出上限截断(UI 提示"已达到输出 token 上限…发送'继续'")时,
本插件自动补一条"请继续";阿里 Token Plan 429/token-limit 触发时,冷却后自动重试;
上下文压缩把一整轮吞掉(检查点刚落、零产出就 completed)时,补一条"照检查点继续动手"。
目标只有一个:输出不断档,不用你手动救场。
安装 / 卸载
dsh plugin --profile web add link:E:/S_Software/deepseek-harness/plugins/dsh-auto-continue
# 装完必须完全重启 dsh web(Settings → Plugins 可见 dsh-auto-continue)
dsh plugin --profile web remove dsh-auto-continue
备份:C:\Users\<USER>\.dsh\profiles\web\cordis.yml.bak-*(dsh plugin 自动生成)。
30 秒自检(不用盯 GUI)
# 1) 插件是否真的挂上了(每次启动一行)
tail -3 ~/.dsh/auto-continue.log
# 2) 是否真的续写过:会话存档里找插件署名的 user/message
python ~/dsh-fixes/scan_auto_continue.py --since "2026-09-05 22:43:00"
auto-continue.log 会记录 已挂载(v0.4.0)…、max-tokens 截断(turn N)→ 自动续写、
检测到 429/token-limit … → Xs 后自动重试(第 k/3 次)、
压缩检查点后零产出收尾(turn N,检查点之后 Xms 就 end 了)→ 补发续写;
不想留文件日志就 DSH_AUTO_CONTINUE_LOG=off(回归测试默认写 off,不污染运维日志)。
机制(对 dsh 0.1.2-rc.1 编译产物实证)
| 假设 | 依据 |
|---|---|
根作用域 ctx.on('agent/status') 收得到所有 agent | agentEvents() 把 agent 融进 payload;官方插件(compaction-basic / goal-round-driver / sdk-jsonrpc-server)同款用法 |
收到 idle 时尾部 turn/end 已落盘 | turn() 的 finally 先 session.append("turn/end"),kick() 的 finally 才 setPhase(idle) |
reason.kind === 'max-tokens' / reason.error.{message,code} | dsh-session TurnEndReasonMap、LlmFailure |
agent.followup(msg) 必须传完整 message | followup → send → inbox.splice(...,[message]),字符串会被当成残缺消息 |
inbox.hasPending 是 boolean getter | dsh-agent Inbox(不是函数,别写 typeof === 'function') |
插件目录解析不到 @deepseek-ai/dsh-llm | 实测 ERR_MODULE_NOT_FOUND(profile 的 node_modules 无 @deepseek-ai 作用域)→ v0.3.1 自带完整消息构造 |
| service 取值必须留在事件派发那一帧内 | 一旦 await 过再调 agent.followup(),cordis proxy 在 fiber.store 取不到 inject 服务,抛 cannot get required service "agents" in inactive context(ac-rig 实测)→ 所以消息构造同步、零依赖,max-tokens 分支同帧投递,只有 429 冷却才用定时器 |
真实 429 的 error.code 是 QUOTA 不是 RATE_LIMIT | 本机 turn/end 语料:429: {"message":"Allocated quota exceeded … #token-limit","code":"insufficient_quota"};而 Free quota exhausted / 余额不足 也是 QUOTA 却不可恢复 → 判定同时看 code 与文案,并用 DEAD_QUOTA 白名单排除 |
压缩检查点是一条 user/message,形状随运行时版本变 | v4(0.1.7+):{kind:'compact-checkpoint'}(dsh-compaction 的 COMPACT_CHECKPOINT_MARKER,且 v3→v4 迁移把历史 plugin:'compact' 行也改写成它);v3 及更早:{kind:'plugin', plugin:'compact'}。两种都认(v0.4.2) |
v4 写侧硬拒 source.kind === 'plugin'(v0.4.2 的靶子) | 0.1.7 起 assertV4MessageSources/assertV4SourceRowAdmission(dsh-session-persistence-jsonl/lib/worker.cjs:10902/11683)对消息源只认 producer 自有 kind;旧形状注入会在下一轮开头被拒:"format v4 message requires a producer-owned source kind"(GUI 报本轮运行失败,存档无痕——被拒的是新事件)。本机 2026-09-25 18:56 实证于 session-8c256625(turn 12 吃 429 后插件补投那一发)。修法与官方迁移器一致:未发布插件的 kind 写 plugin:<name>(dsh-session-format-v3-to-v4/lib/index.js:92 同款改写形) |
| 压缩能在轮内把整轮吞掉(v0.4 的靶子) | 本机 69 份存档 29 次压缩实测:13 次照常干活、7 次"回一句即 max-tokens"(旧分支已覆盖)、3 次 turn/start → user[plugin:compact] → turn/end[completed] 间隔仅 10~21ms 且零回复零工具。compaction-basic 自己就在 log 里写 shadowed N surface nodes:open-turn 压缩事务会 shadow 掉排在前的 surface 节点,本来该在这一轮跑的续写就此蒸发。这种 turn/end 的 kind 是 completed,跟正常收尾一模一样,只能靠形状认 |
max-tokens 有两种,只有一种该补枪(v0.4.1 的靶子) | 本机 2026-09-22 存档 session-28de2f17 回合 12~18:assistant/message.usage.outputTokens 全是 1、stopReason: "length"、正文只有一个 reasoning token「I」。真因在配置:settings.yaml 的 llm-pi-ai.providers.tokenplan 只写 id+name,适配器按 entry.maxTokens ?? base?.maxTokens ?? defaultContextWindow/defaultMaxTokens 取值(dsh-llm-pi-ai/lib/index.js:670-672,内置默认 262144 / 32768),而 pi-ai 自带目录里同一个 baseURL (qwen-token-plan-cn.json)这 8 个模型全是 1000000。于是 clampMaxTokensToContext(pi-ai/dist/api/simple-options.js:7-10)算出 262144 - 430206 - 4096 < 0 → max_tokens 被夹成 1;同一份错窗口还把正常回复误判成 pi-ai detected context overflow,压缩自己的总结请求同样超限 → 11 次压缩只有 1 次落地。旧版把这形状当「长答案被截断」,5 分钟连补 6 枪、每发重带 430k 输入,最后真打成 429 insufficient_quota。判据只看这一轮那条 assistant 的 usage.outputTokens ≤ 8 → 不续写、一轮只喊一次 |
行为与守卫
- 触发:回合
max-tokens截断、429 / Allocated quota exceeded / token-limit / insufficient_quota、 或压缩检查点之后零产出就completed(三者共用同一套额度与守卫); - 每 agent 每 60 秒最多
DSH_AUTO_CONTINUE_MAX(默认 4)次自动续写,额度在排期时就预占(v0.3 是发送后才计数,突发会冲破上限); - 429 熔断:连续自动重试最多
DSH_TP_429_MAX_RETRIES(默认 3)次,之后停手等你手动继续—— 额度真尽时不会每分钟 4 次地无限重试;期间任何一次正常推进(completed)自动清零; - 新鲜度窗口
DSH_AUTO_CONTINUE_STALE_MS(默认 10 分钟):重启/恢复老会话时, 不会隔几小时突然补一句"请重新执行我上一条请求"; - 尊重你正在打的字:
inbox.hasPending为真、或 agent 已被别的输入唤醒(非 idle)就放弃这一发并让出额度; - 全局错峰:自动消息之间至少间隔
DSH_TP_PACING_MS(默认 2.5s)+ 0~1.5s 抖动,平滑多会话的瞬时 TPM; - 429 恢复文案里的"已等待 X 秒"取真实计算值(v0.3 的
Math.max(C-p, C)恒等于 C,pacing 白写); - 任何异常只记日志,绝不打断 agent 主流程。
环境变量
| 变量 | 默认 | 说明 |
|---|---|---|
DSH_AUTO_CONTINUE | 1 | 0 整体关闭 |
DSH_AUTO_CONTINUE_MAX | 4 | 每 agent 每 60s 最大续写次数 |
DSH_AUTO_CONTINUE_STALE_MS | 600000 | 只处理 N 毫秒内的 turn/end |
DSH_TP_429_COOLDOWN_MS | 60000 | 429 冷却(从 turn/end 时刻起算) |
DSH_TP_429_MAX_RETRIES | 3 | 连续 429 自动重试上限(熔断) |
DSH_TP_PACING_MS | 2500 | 自动消息全局最小间隔(另加抖动) |
DSH_TP_REMINDER | 1 | 429 恢复消息带"仅本会话提示"文案 |
DSH_AUTO_CONTINUE_CP_WINDOW_MS | 60000 | 检查点 → turn/end 的最大间隔,超过就不算"压缩吞轮"(调小更保守,实测真样本是 10~21ms) |
DSH_AUTO_CONTINUE_CLAMP_TOKENS | 8 | 一轮输出 ≤ 这个 token 数判成「输出预算被夹死」,不续写(一轮只记一条 warn) |
DSH_AUTO_CONTINUE_LOG | ~/.dsh/auto-continue.log | 行日志路径;off/0/空 关闭 |
回归测试(0 token,不连模型)
npm test # 18 例:max-tokens 续写 / 429 冷却重试 / 熔断 / 上限 / 去重 / 消息完整性
# + 6 例压缩吞轮(补枪、有产出不插手、超窗不插手、同轮只一次、
# max-tokens 优先走旧分支、无检查点的 completed 保持安静)
# + 2 例输出夹死判定(只回 1 个 token 不补枪、真截断照常补)
测试默认 DSH_AUTO_CONTINUE_LOG=off,不会往 ~/.dsh/auto-continue.log 里灌假 agent 的决策行。
与官方 goal 的分工
goal(create_goal / /goal)才是"多轮自主推进"的正道:它有 dsh-goal-round-driver
在每次 idle 时排下一轮,且能活过上下文压缩——SessionStartSource 类型里虽然写了
'compact',但 0.1.2-rc.1 的编译产物里没有任何调用方传它(只有 "startup" 与 "resume"),
而 dsh-goal 恰恰只在 agent/session-start 上无条件 activation = "disarmed"。
也就是说:同进程压缩不会解除 goal 的续期权,跨进程恢复才会(那种情况要人再 resume 一次)。
本插件管的是另一半:goal 只在你显式建了目标的会话里工作,而日常"我就想让它把这一件事做完" 的会话没有 goal,被 max-tokens / 429 / 压缩事务打断后就只能靠这里补一枪。 本机现状也印证两者互不重叠:5 份用过 goal 的存档一次都没触发压缩,29 次压缩全在没 goal 的长会话里。
版本
- v0.4.2:适配 0.1.7 / 会话格式 v4 的两道硬闸——注入消息的 source 改
{kind:'plugin:dsh-auto-continue'}(旧形kind:'plugin'被写侧拒绝,真机 2026-09-25 打死过 KB 会话的 429 重试补投),压缩检查点识别加收{kind:'compact-checkpoint'}(0.1.7 现写与迁移后存档的形状,旧形保留)。要重启 dsh web 才进运行时。 - v0.4.1:加「输出预算被夹死」不补枪的闸(机制表最后一行),免得把配置错误放大成 6 发 × 430k 输入的烧钱循环;顺手修
npm test(node --test test/在 node v24 下报 MODULE_NOT_FOUND)。 - v0.4.0:新增"压缩吞轮"补枪(形状判定,见机制表最后一行);测试默认关文件日志,不再污染运维日志。
- v0.3.1:修 3 个实测缺陷(残缺消息缺
role/id、hasPending守卫恒假、突发冲破每分钟上限), 新增 429 熔断、新鲜度窗口、idle 二次确认、行日志与回归测试。 - v0.3:改为根作用域
agent/status+ 读尾部turn/end(v0.2 的 agent 作用域session/event收不到事件,故完全失效)。 - v0.2:加 429 冷却重试与并发错峰。
说明
- 自动续写继续消耗套餐 token(
tokenplan/qwen3.8-flash),属"把订阅用出价值"的预期行为; - 只统计/只影响套餐 provider 之外的会话不做特殊处理:任何 provider 的
max-tokens都会续,429 只对 token-limit 类文案生效; - 若官方将来内置 auto-resume,本插件即可废弃。
Comments
Loading…
Similar plugins
by inmny
为 DeepSeek Harness 增加一个直接续跑按钮。当会话异常结束时,可以从现有上下文继续执行,不会引入其他提示词。
★ 3
MIT
JavaScript
Sep 11, 2026
dsh plugin --profile web add dsh-plugin-continueby elanski
Unified auto-continue + model failover plugin for DeepSeek Harness (RU/EN settings card)
★ 0
MIT
TypeScript
Sep 22, 2026
dsh plugin --profile web add dsh-failover-continueby Frog755
DSH client plugin: auto-sends 「继续」 when a turn is interrupted/errored/overlong. No model/provider switching. Ships with a settings card.
★ 3
↓ 144/wk
MIT
JavaScript
Sep 10, 2026
dsh plugin --profile web add @frog755/dsh-client-auto-retryby tianyaojiudi-prog
DSH(DeepSeek Harness) 插件:把一段可在设置页随时改写的文字,作为全局系统提示词段注入到所有会话,含子代理与工作流内部子代理;文本与开关即时生效,无需重启。
★ 8
MIT
JavaScript
Sep 18, 2026
dsh plugin --profile web add dsh-djy-xttscby haochi72
Automatically sends "continue" to recover from RATE_LIMIT (429) and QUOTA (insufficient_quota) errors. Per-session consecutive-failure counter (default 20, configurable 1-100), a toolbar on/off switch
★ 2
↓ 71/wk
MIT
JavaScript
Aug 19, 2026
dsh plugin --profile web add dsh-auto-continue-429by zchuxi
DSH Web 插件:三段式回合恢复(同轮静默重试 / 输出超限同轮续写 / 可选新一轮继续),输入框为空时发送键变成「继续」。A three-tier recovery policy for broken turns for DSH.
★ 0
MIT
JavaScript
Oct 1, 2026
dsh plugin --profile web add dsh-restart-task