fix(bridge): 支持无回复回合静默结束#554
Conversation
deepcoldy
left a comment
There was a problem hiding this comment.
独立复审结论:无阻塞,建议 rebase 到最新 master 后合入
在最新 master 的合并树上实测(build 绿 + bridge-fallback-gate/prompt-builder/cli-adapters 374 用例全过 + 自写对抗/组合测试),核对如下。
1. 需要先 rebase:与 master 上已合入的 #553 文本冲突(非语义冲突)
PR base 落后 master。master 已落地 shouldEmitEmptyCompletedBridgeFallback(#553,空完成兜底),与本 PR 在 src/services/bridge-fallback-gate.ts 和 test/bridge-fallback-gate.test.ts 的 import 处文本冲突。我在合并树上解掉冲突后验证两者语义完全兼容:
emitReadyCodexTurns里finalText.trim()==='BOTMUX_NO_REPLY'落入"非空 content"分支 → 随即被shouldSuppressBridgeEmit(本 PR 新增的isBridgeNoReplyFinal)命中 → suppressed,不会触发 #553 的空完成诊断;- 反向:
shouldEmitEmptyCompletedBridgeFallback对 sentinel 返回 false(它内部!shouldSuppressBridgeEmit=!true)。
两者不会互相误触发。只需 rebase 解 import 冲突即可,无需改逻辑。
2. 关于"sentinel 未覆盖所有 CLI"——同意不作为阻塞(且不构成 leak 回归)
关键问题是:是否存在"既收到 sentinel 指令、其 final 又走未经 gate 的自动转发"的 CLI?——若有,会把字面量 BOTMUX_NO_REPLY 泄漏进聊天,那才是新回归(比现状更差)。逐路径核对结论是不存在:
- Claude 家族(claude-code/genius,
claudeDataDir标记)→emitReadyTurns→shouldSuppressBridgeEmit已 gate ✓ - 结构化 bridge(codex/traex/coco/hermes/mtr/pi/grok)→
emitReadyCodexTurns→ 同一 gate ✓ - 无 transcript bridge 的 CLI(gemini/opencode/aiden/antigravity/kimi/kiro-cli/oh-my-pi/copilot/mir/mira、以及非 adopt 的 cursor)→ final 本就只在终端、从不自动转发,输出 sentinel 是 no-op,不会进聊天 ✓
- codex-app:developerInstructions 显式让模型忽略旧的 botmux send 提示,收不到 sentinel;且其 final 分支本身在
startedAtMs!==undefined时也过 gate ✓
因此未覆盖的 CLI 只是维持"可能多发"的现状,不是本 PR 引入的缺陷。撤回 P1 的判断正确。
3. "本轮零输出"不是新状态
模型正常调用 botmux send 时,shouldSuppressBridgeEmit 今天就已返回 true → 不发 final_output,turn 照常 turn_terminal completed。"turn 完成但零 final_output"是每个用了 botmux send 的回合的现状,daemon 早已正确处理。sentinel 只是多一条到达同一状态的路径,无新的 daemon 侧失败面。
4. 精确匹配的设计是对的
trim()=== 全等 + 大小写敏感。对抗验证:botmux_no_reply、`BOTMUX_NO_REPLY`、BOTMUX_NO_REPLY.、BOTMUX_NO_REPLY\n\n(说明) 全部不被吞,只有纯 sentinel(含首尾空白)被吞。adopt 模式正确排除。
建议(非阻塞)
- rebase 到最新 master(解 #553 import 冲突);
- PR 描述影响范围补一句"自定义投递/提示链路的 CLI(hermes/mira/mir/codex-app/riff)暂不纳入本静默协议",避免后续误读。
验证:合并树 pnpm build 通过;目标 3 文件 374 用例全绿;git merge-tree 仅 import 段文本冲突,已验语义兼容。
a4afcf0 to
b2c432e
Compare
已代作者 rebase 到最新 master(应维护者/仓库 owner 要求)原 head 处理:
已验证 #554(sentinel)与 #553(空完成兜底)语义兼容:sentinel final 在 验证结果(rebase 后的
PR 现为 MERGEABLE(BLOCKED 仅因缺一个 approval)。 |
背景 / 动机
Botmux 会在模型未调用
botmux send时,将 transcript 中的 final answer 兜底转发到飞书,避免有价值的回答静默丢失。但当模型判断本轮无需回复、又在 final 中解释“保持沉默”时,这段解释仍会被 fallback 当成正常回答发出。现有协议缺少一个机器可识别的“本轮正常完成但无需用户可见回复”结果;仅依靠“没信息量就不发”的自然语言提示无法与 final fallback 正确配合。
改动
botmux send,final 只输出BOTMUX_NO_REPLY,且不解释沉默原因。BOTMUX_NO_REPLY的 final 静默处理,同时仍保留 turn 完成语义。BOTMUX_NO_REPLY的其他正文仍按正常 final 转发。/adopt会话保持原语义,不解释该标记,避免改变外部 CLI 会话的原生输出。默认值 / 兼容性依据
无需新增配置,协议默认对 botmux-aware 的非 adopt 会话生效。原有
botmux sendmarker 门控和 transcript final fallback 均保留;只有精确 sentinel 命中时新增静默分支。测试覆盖
验证
pnpm vitest run test/bridge-fallback-gate.test.ts test/prompt-builder.test.ts test/cli-adapters.test.ts:374 passedpnpm vitest run test/codex-bridge-queue.test.ts test/bridge-final-output-retry.test.ts test/claude-turn-terminal-contract.test.ts:89 passedpnpm build:通过git diff --check:通过pnpm test:9839 passed、22 skipped、8 failed;对其中持续失败的 4 个文件在本分支与未修改的origin/master上分别单进程复跑,均为相同的 7 个既有失败(172 passed、5 skipped),涉及 workflow 文件系统清理、distillation 环境及 VC meeting 测试,与本改动无差异影响范围
改动涉及共享 prompt 文案与 transcript bridge fallback gate,覆盖 TraeX、Codex、CoCo、Claude 等使用该公共路径的非 adopt 会话。无需迁移配置,不改变显式
botmux send、普通 final fallback、adopt 会话或不使用 transcript bridge 的消息路径。