### What happened / 实际发生了什么 一次模型响应里同时返回两个调用:`generals_set_intent` 和 `vtuber_act`。`generals_set_intent` 先执行,Core 的 barrierAfter 规则要求先复核它的结果;`vtuber_act` 因此被记为未执行。vtuber 的 `outputTap` 在这个判定之前,已经于 `vtuber_act` 的 `response.output_item.added` 开始开演: ``` [not executed: review the preceding tool result first] ``` 模型收到这条回执后又发了一次相同的 `vtuber_act`,两次都合成出 6880ms 音频并进入演出。当前 run 里同类“未执行”的 `vtuber_act` 有 28 次,其中两对是同一脚本的完全重复。 ### What you expected / 期望的行为 Core 已经判定不会执行的调用,不应在此之前被交给 `outputTap` 产生不可撤销副作用;或者 tap 必须在副作用发生前收到“不会执行”的信号。现状是 tap 在 `output_item.added` 就外化,Core 在响应收束后才拒绝,契约不成立。 ### How to reproduce / 复现步骤 在 Core 的 fixture 里: 1. 定义一个带 `barrierAfter: true` 的工具 `stage`,以及一个普通工具 `after`。 2. 给这个 World 一个 `outputTap`,其 `externalizes` 在 `response.output_item.added` 时对 `after` 返回 true,并用 `onEvent` 记录收到的 `tool_call.begin`。 3. 让模型在同一条响应里依次返回 `[stage, after]`。 4. 观察 tap 收到 `after` 的开始事件;而 session 里 `after` 的回执是 `[not executed: review the preceding tool result first]`。 5. 如果 tap 的副作用是播放一段台词,模型看到回执后重发 `after`,这段台词就会播放两次。 ### Run directory lines / 运行目录日志 ```text 2026-11-01T12:06:29.660+08:00 worlds.vtuber.round round-open #132 2026-11-01T12:06:29.664+08:00 toolcalls tool=generals_set_intent 2026-11-01T12:06:30.018+08:00 function_call vtuber_act call_01_… 2026-11-01T12:06:30.020+08:00 function_call_output "[not executed: review the preceding tool result first]" 2026-11-01T12:06:30.896+08:00 worlds.vtuber.round round-open #133 2026-11-01T12:06:31.060+08:00 function_call vtuber_act call_00_…(同一 script) 2026-11-01T12:06:32.013+08:00 tts stream-received 2025ms → 6880ms 音频 2026-11-01T12:06:33.671+08:00 tts stream-received 1658ms → 6880ms 音频 两条 vtuber_act 的脚本相同;第一次的回执是未执行,第二次才执行。日志里没有密钥;已去掉游戏与玩家标识。 ``` ### Commit or npm version / commit 或 npm 版本 v0.2.11 (78e4350); local checkout 0979540 ### Diff against the release / 与对应 Release 的 diff 相关文件的 diff 为空;本地 checkout 的其它改动是 bot 装配与平台相关改动,不涉及 src/core/loop.ts。附件 release-diff.txt 记录了检查与空 diff;候选补丁见 output-tap-barrier.patch。 [output-tap-barrier.patch](https://github.com/user-attachments/files/34234572/output-tap-barrier.patch) [release-diff.txt](https://github.com/user-attachments/files/34234576/release-diff.txt) ### OS and Node version / 操作系统与 Node 版本 Windows 10 专业版 19045;Node.js v24.20.0
Activity
Phantivia commentedon Nov 4, 2026
@Lantichr 谢谢这份报告和复现,查得非常准。我们核了自己直播的 5 场恶魔轮盘:被屏障判成未执行的演出 212 次,其中 196 次其实已经开播,同一句播了两遍的有 23 处。
修法在 #374,没照你的补丁做,原因是我们对规则的看法不同:说话、字幕这类输出不是「要先看到前一步结果才能做」的行动,所以它们不该归屏障管。现在工具定义多了一个
streamsOutput标记,意思是接收器在模型还在输出时就会对外执行这个工具;标了它的调用不受屏障影响,在屏障工具执行完之后照常执行,回执是它自己的真实结果;没标的照旧跳过,被跳过的回执里会写明同一条回复里哪些流式工具已经照常输出。你的复现用例改了断言后放进了测试,提交带了你的署名。cortico-world-vtuber 那边会给
vtuber_act加上这个标记(Pal-AI-Lab/cortico-world-vtuber#14)。随下个版本发布。