流式 MessageDisplay
按累计快照处理回复,正确处理最终投递、并发完成顺序和取消。
MessageDisplay 在回复流式输出期间重复触发,适合实时显示或增量记录。它不支持 matcher,也不接受控制输出,返回值和退出码都被忽略。
输入是累计文本
| 字段 | 含义 |
|---|---|
| message_id | 整条流式消息稳定标识 |
| displayed_text | 到当前时刻的完整累计文本,不是增量 |
| is_final | 该消息最后一次投递为 true |
中途事件最多约每 200ms 一次,最终事件不受该防抖窗口延迟。包含工具的轮次可能有多条消息,每次模型调用各有 ID 和最终事件;只有工具调用、没有显示文本的响应不发出此事件。
慢消费者会跳过中间快照
每条消息最多一个中途执行在进行中;运行期间新快照替换待发旧快照,不排成长队列。由于每次是累计文本,不需要拼接遗漏的片段。
最终事件在消息结束时立即发送,即使中途执行尚未完成,两者也可以重叠。结束顺序不保证:旧中途执行可能比 final 更晚完成,甚至晚于 Stop。消费者应按 message_id 把 is_final 当作终态,不要用“最后完成的进程”覆盖最新文本。
五秒等待不是无限交付保证
轮次结束和 Stop 最多等最终投递完成五秒。五秒内完成时,无头进程会等待它,Stop 在其后执行;更慢时会向 stderr 警告,TUI/ACP 后台继续,无头进程则先退出。
普通退出不会杀死这条已派发的最终 Hook,它可自行完成。因此 shell 中后续命令可能在慢 Hook 仍写文件时启动,不能以 Qwen 退出码作为“Hook 已写完”的唯一信号。
取消的两个时点
final 派发前已取消时不会发 final;即使文本实际已流完,只要轮末检查前收到 abort,也可能没有最终事件。只在 final 时刷新的消费者应为这种情况设计超时丢弃或明确取消处理。
final 已派发、仍在等待完成时取消,执行进程可能被 SIGTERM 中断。输入已交付不代表副作用已完成。中途 displayed_text 始终是暂定显示状态,只有 final 才代表该消息的最终文本。
TUI、无头和 ACP 使用同一字段契约。记录侧应同时考虑重复快照、旧进程晚完成、缺 final 和已派发但被中断四种情况。