fix(capsule): 润色/插入失败改走 error 态——失败提示不再被前端丢掉 (#874)

胶囊只在 error 态渲染 message,done 态按设计是「冻结光效淡出、不带文字」
(Capsule.tsx 的 VoiceOrbStage:`state === 'error' && <span>{message}</span>`)。

但 end_session 无论成败都发 CapsuleState::Done。于是 default_done_message
辛辛苦苦拼出来的失败文案,全部在前端被丢弃——算了、传了、不显示。

最典型的受害者是润色失败。它会静默回退成未润色的原文,而胶囊照常显示成功态,
界面上没有任何痕迹。实测后果:LLM 凭证失效后连着十几个小时每一句都在拿原文,
用户只能靠「今天出来的字怎么变笨了」察觉,而日志里其实每一句都 ERROR 了。
default_done_message 的注释写着「polish 失败优先告知用户」——意图是对的,
只是这句话从来没能显示出来。

改成按结果选状态:润色失败 / 插入失败 / TSF 未上屏走 error 态,成功仍走 done。
文案生成逻辑一行没动,只改用哪个状态发出去。

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bigsongandClaude Fable 5 authored and GitHub committed 2026-08-02 11:53:01 +08:00
1 parent 83c4f7f4e9
commit 95edf8943b
1 file changed
+16 -1
@@ -3794,9 +3794,24 @@ pub(super) async fn end_session(inner: &Arc<Inner>) -> Result<(), String> {
default_done_message(status, polish_error.is_some())
};
// 胶囊只在 error 态渲染 message —— done 态按设计是「冻结光效淡出、不带文字」
// (见 Capsule.tsx 的 VoiceOrbStage:`state === 'error' && <span>{message}</span>`)。
// 所以失败信息必须走 error 态才看得见,否则文案算出来就被前端丢掉。
//
// 最典型的受害者是润色失败:它会静默回退成未润色的原文,而胶囊照常显示成功态,
// 用户界面上没有任何痕迹。实际后果是 LLM 凭证失效后,用户连着十几个小时每句话
// 都在拿原文,只能靠「今天出来的字怎么变笨了」察觉,日志里其实每一句都报了错。
let session_failed =
tsf_required_insert_failed || polish_error.is_some() || status == InsertStatus::Failed;
let capsule_state = if session_failed {
CapsuleState::Error
} else {
CapsuleState::Done
};
emit_capsule(
inner,
CapsuleState::Done,
capsule_state,
0.0,
elapsed,
done_message,