dsh-auto-continue-inturn
DSH companion plugin: when the model is truncated by max-tokens, keep the turn alive and continue writing instead of waiting for a manual "continue"
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 24, 2026
- Updated
- Oct 24, 2026
Introduction
dsh-auto-continue-inturn
DeepSeek Harness(DSH)伴生插件:模型被输出上限截断时,在同一轮内静默续写——不弹「已达到输出 token 上限」提示条,也不新起回合等人手动发「继续」。
English | 简体中文
它解决什么
上游模型撞到输出上限时,DSH 内核会把 turn/end 的原因记为 max-tokens,前端随即渲染一条黄色警告条并停下,等你说「继续」。一个本来能一次写完的长回答,被切成好几段,每段之间都要人肉点一次。
本插件在轮次真正关闭之前介入,把这一步续上:模型的输出接着往下写,你看到的是同一轮里一条连续的回答。
和「跨轮自动发送」插件的分工
社区里另一类做法是等回合结束后再自动发一条「继续」。两者不冲突,覆盖的场景不同:
| 场景 | 谁处理 |
|---|---|
max-tokens 截断(同轮前 N 次) | 本插件:同轮静默续写,不新增回合 |
max-tokens 截断(超过本插件上限后) | 跨轮兜底插件接续 |
| 网络错误 / 超时 / 5xx / 429 | 跨轮兜底插件 |
| 空转循环、重复工具调用 | 跨轮兜底插件的 loop guard |
只装本插件也能单独工作;配合跨轮兜底插件,长任务的恢复率更完整。
机制(全部走官方扩展点,不改内核文件)
- 监听
agent/assistant-stream,记住该 agent 最近一次 assistant attempt 的finish.reason.kind,以及这次 attempt 里有没有新的流式活动。 - 监听
agent/turn-stopping(签名{ agent, turn, signal },serial 派发)。官方文档原话:「agent/turn-stopping在本可完成的轮次关闭前运行,并可通过 steer 使其保持打开」。 - 若终止原因正是
max-tokens且未超限,调用agent.steer(notice)把续写提示放进next-step收件箱。内核看到inbox.nextStep.length !== 0就不会break,于是同一轮继续生成。 - 单轮内自动续写次数受
maxInTurn(默认 5)约束;超限后放行本轮结束,交给跨轮兜底或人工接手。
因为是「同一轮内续写」,续写不会往会话里塞新的用户消息,也就不存在「每 32K 就多一条『继续』污染上下文」的问题。
防护
自动替用户发消息属于能动会话的高危行为,所以默认开了三道闸:
- 空转防护(
idleGiveUp,默认 2):上一次续写之后若完全没有新的流式活动,记一次空转;连续 2 次空转就停手。避免模型陷入「截断 → 续写 → 立刻又截断」把次数和 token 一起烧光。判定只认「start/finish之间有没有任何帧」,不猜 chunk 类型词汇表,因此内核换帧类型也不会误判。 - 放弃后同轮不再重试:一旦放弃(达上限或连续空转),该轮打上「已停手」标记。否则计数清零后下一次截断又会从头再续,形成「放弃 → 重置 → 再续」的循环。
- 用户与子代理优先:
signal.aborted为真(你按了停止)直接不注入;子代理会话(session.header.origin === "subagent")一律不接管,由父代理管理。
另外 agent.steer 返回 false 视为投递被拒,不计数、立即停手。
配置
写在 profile 的 cordis.patch.yml 该条目 config 下:
| 字段 | 默认 | 说明 |
|---|---|---|
enabled | true | 关掉后完全不注册行为 |
maxInTurn | 5 | 单轮最多自动续写次数(正整数) |
idleGiveUp | 2 | 连续多少次「续了但毫无产出」就停手;0 关闭空转防护 |
text | 见下 | 续写提示词 |
summary | 自动续写:紧接上文继续 | 消息 source.summary,会话记录里可审计 |
log | true | 是否输出 [dsh-auto-continue-inturn] 日志 |
默认 text:
上条回复因达到输出 token 上限被截断,请紧接末尾无缝继续输出,禁止重复上文。
非法配置一律 fail loud(抛错,而不是静默退回默认值)——配置写错时你立刻知道,而不是在长任务里发现行为不对。
安装
前提:DSH 已安装,且知道自己的 DSH_HOME(默认 ~/.dsh 或 Windows 下的 %DSH_HOME%)。
-
把本仓库目录放进 profile 的
node_modules(真实目录,不要用 junction——同级@deepseek-ai的 peer 依赖要靠它解析):<DSH_HOME>/profiles/<profile>/node_modules/dsh-auto-continue-inturn/ -
在该 profile 的
cordis.patch.yml里加一条注册:- insert: - id: auto-continue-inturn name: 'dsh-auto-continue-inturn' config: maxInTurn: 5 idleGiveUp: 2 -
完全退出 DSH 再启动(热加载是否生效取决于 profile 的
patchReload,重启最稳)。
验证
cd dsh-auto-continue-inturn
node test/self-test.mjs
26 项自检不依赖 DSH 运行时,用假 ctx / 假 agent 覆盖:配置校验、纯决策、上限与空转、轮次切换重置、放弃后不重试、steer 返回 false、子代理豁免、中止豁免、多 agent 隔离、消息 source 合规与冻结语义。
想验证真实行为:连续触发两次很长的输出(把 max_tokens 调小最快),观察第二轮截断时不再出现黄色提示条、回答在同一条消息里继续。
卸载 / 回滚
- 从
cordis.patch.yml删掉上面那条- insert:块(连同上方注释)。 - 删除
profiles/<profile>/node_modules/dsh-auto-continue-inturn/。 - 重启 DSH。
只删目录不删注册行,DSH 启动时会因解析不到包而报错;两处要一起改。
兼容性
- DSH 0.2.x(会话日志 v4)。消息
source.kind用生产者自有名(非字符串"plugin"),form: "notice"配summary,符合 v4 格式校验。 - 零静态依赖:
@deepseek-ai/dsh-llm/message只做可选动态 import;解析不到时退回与官方createUserMessage同形的冻结消息对象,因此不装@deepseek-ai/dsh-llm也能跑。
隐私
插件不联网、不收集任何数据、不写插件目录以外的文件。它唯一做的事是:在你本来就会收到「继续」提示的地方,替你把那一句发出去,并把次数限制在配置范围内。
许可
MIT,见 LICENSE。