Skip to content

v0.1.26 — 折叠不再吃掉仍在飞的那一轮(修重复回答)

Choose a tag to compare

@ScreamingMaggot ScreamingMaggot released this 30 Sep 07:54
· 12 commits to master since this release

症状

思考强度 off(启用外置推理)的会话里,压缩未完成时发送用户消息,主对话对同一条消息回复两次。

证据(真机 session-de4959d2,回合 2)

seq247  15:47:07  user/message  src=user           "怎么样你觉得好玩吗"
seq251  15:47:10  assistant/message                "好玩。而且我觉得这一轮…"     ← 第一次回答
seq254  15:47:21  user/message  src=plugin:CONTEXTinjector
                  surfaceOp = replace 247..251    ← 转录吞掉了本轮的一问一答
seq255  15:47:21  step/start   turn=2 step=2
seq259  15:47:24  assistant/message                "好玩。不是客套。……"          ← 第二次回答同一条

根因

closedPrefixEnd 在"所有 tool-call 都配到 result"时返回表面末端。于是当 pre-step 折落在本轮第 2 步(本轮首个工具已闭合)时,遮蔽集覆盖到本轮末尾 → 本轮的 user 消息和刚产出的助手回复被一起替换成转录 → 表面尾部变成 user 角色节点 → 宿主读作"未回答的用户轮" → 再跑一步 → 重复回答。

代码里此前没有任何"进行中内容不进遮蔽集"的判据 —— 而设计文档里那句"只折叠已经彻底结束的内容"是有的。本版把它从"靠 closed-prefix 的偶然性满足"变成显式守卫。

修法

新增纯函数 openRoundClampEnd(entries, session, end):

  • 找最后一个真人轮起点(plugin:CONTEXTinjector 自身节点不被误认);
  • 该起点之后有 turn/end ⇒ 该轮已结束 ⇒ 不钳制(轮末折的主路径不受影响);
  • 没有 ⇒ 仍在飞 ⇒ 遮蔽终点钳到该起点之前(钳成 <0 则本轮不折);
  • 事件不可得/异常 ⇒ 不钳制,退回旧行为(不让折叠因查询失败永久停摆)。

判据与既有 collectDeadCalls 的"该轮已结束 ⇔ 其后有 turn/end"同源,不引入新语义。

同一会话五笔折叠用新守卫重判:seq254、seq275(两笔吃掉当前轮的)被钳制;seq244、seq245、seq262(折已结束内容)照常放行。

验收

  • tests/round-guard.test.mjs:7 条断言全绿(仍在飞→钳制 / 已结束→不钳制 / 无轮起点→不钳制 / 自身节点不误认 / 轮起点在 0 → 延后 / 事件不可得→回退 / end<0 原样返回)
  • 同仓另两套回归仍全绿:漂移 #11(18 断言)、toolfold 单行契约(16 断言)
  • 三份副本(安装位 / 仓库 / 交付包)同步,node --check 通过

⚠️ 装好需重启 dsh web 才生效(插件在内存里)。