Skip to content

fix(bridge): 支持无回复回合静默结束#554

Open
xiongz-c wants to merge 1 commit into
masterfrom
codex/no-reply-sentinel
Open

fix(bridge): 支持无回复回合静默结束#554
xiongz-c wants to merge 1 commit into
masterfrom
codex/no-reply-sentinel

Conversation

@xiongz-c

Copy link
Copy Markdown
Collaborator

背景 / 动机

Botmux 会在模型未调用 botmux send 时,将 transcript 中的 final answer 兜底转发到飞书,避免有价值的回答静默丢失。但当模型判断本轮无需回复、又在 final 中解释“保持沉默”时,这段解释仍会被 fallback 当成正常回答发出。

现有协议缺少一个机器可识别的“本轮正常完成但无需用户可见回复”结果;仅依靠“没信息量就不发”的自然语言提示无法与 final fallback 正确配合。

改动

  • 在中英文首轮 routing 提示和短 follow-up reminder 中加入精确静默协议:无需回复时不调用 botmux send,final 只输出 BOTMUX_NO_REPLY,且不解释沉默原因。
  • 非 adopt transcript bridge 对去除首尾空白后精确等于 BOTMUX_NO_REPLY 的 final 静默处理,同时仍保留 turn 完成语义。
  • 不做关键词或自然语言推断:包含 BOTMUX_NO_REPLY 的其他正文仍按正常 final 转发。
  • /adopt 会话保持原语义,不解释该标记,避免改变外部 CLI 会话的原生输出。

默认值 / 兼容性依据

无需新增配置,协议默认对 botmux-aware 的非 adopt 会话生效。原有 botmux send marker 门控和 transcript final fallback 均保留;只有精确 sentinel 命中时新增静默分支。

测试覆盖

  • 覆盖精确 sentinel(含首尾空白)静默、解释性正文不误吞、adopt 模式不解释 sentinel。
  • 覆盖 TraeX 首轮 system hints 和普通 follow-up reminder 均包含静默协议。
  • 回归 structured bridge queue、turn terminal 和 final-output delivery 相关路径。

验证

  • pnpm vitest run test/bridge-fallback-gate.test.ts test/prompt-builder.test.ts test/cli-adapters.test.ts:374 passed
  • pnpm vitest run test/codex-bridge-queue.test.ts test/bridge-final-output-retry.test.ts test/claude-turn-terminal-contract.test.ts:89 passed
  • 远端 Linux 隔离目录执行 5 个相关测试文件:424 passed
  • 本地及远端 Linux 隔离目录执行 pnpm 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 的消息路径。

@xiongz-c
xiongz-c requested a review from deepcoldy as a code owner July 22, 2026 07:37

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

独立复审结论:无阻塞,建议 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.tstest/bridge-fallback-gate.test.ts 的 import 处文本冲突。我在合并树上解掉冲突后验证两者语义完全兼容:

  • emitReadyCodexTurnsfinalText.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 标记)→ emitReadyTurnsshouldSuppressBridgeEmit 已 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 段文本冲突,已验语义兼容。

@deepcoldy
deepcoldy force-pushed the codex/no-reply-sentinel branch from a4afcf0 to b2c432e Compare July 26, 2026 12:12
@deepcoldy

Copy link
Copy Markdown
Owner

已代作者 rebase 到最新 master(应维护者/仓库 owner 要求)

原 head a4afcf02 落后 origin/master(d3c4a298)155 个提交,与已合入的 #553(shouldEmitEmptyCompletedBridgeFallback,空完成兜底)在 test/bridge-fallback-gate.test.ts 的 import 段文本冲突。

处理:

  • 将本 PR 的单个提交 a4afcf02--onto origin/master 重放,保留原作者 xiongz-c;
  • 唯一冲突是 import 行,已按合并后应有形态解决(同时导入 BRIDGE_NO_REPLY_SENTINELshouldEmitEmptyCompletedBridgeFallback,无重复),逻辑零改动;
  • --force-with-lease 推到 PR 分支,新 head b2c432e8

已验证 #554(sentinel)与 #553(空完成兜底)语义兼容:sentinel final 在 emitReadyCodexTurns 落入非空 content 分支后被 shouldSuppressBridgeEmit 命中而 suppressed,不会触发 #553 的空完成诊断;反向 shouldEmitEmptyCompletedBridgeFallback 对 sentinel 亦返回 false。

验证结果(rebase 后的 b2c432e8):

  • pnpm build:通过
  • pnpm vitest run bridge-fallback-gate / prompt-builder / cli-adapters / bridge-final-output-retry / codex-bridge-queue / claude-turn-terminal-contract:6 files / 473 tests 全通过
  • git diff --stat origin/master..HEAD:6 文件 +50/−7,即本 PR 原始 delta(不含任何 master 提交)

PR 现为 MERGEABLE(BLOCKED 仅因缺一个 approval)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants